Particle Groups implemented

This commit is contained in:
Esophose 2018-10-16 19:09:20 -06:00
parent 4fce663167
commit 88dcf75ace
14 changed files with 328 additions and 61 deletions

View file

@ -2,6 +2,26 @@
* TODO: v5.3
* + Add new style 'tornado'
* + Add new style 'companion'
* * Setting in config.yml for max number of particle groups, default 10
* * Permission to allow players to overrule the max particle groups allowed in the config playerparticles.groups.unlimited
* * Setting in config.yml to disable non-event styles while the player is moving
* * Setting in config.yml for max particles allowed per player, default 3
* * Permission to allow players to overrule the max particles allowed playerparticles.particle.max
* * Permissions for the following:
* - playerparticles.particles.max.1
* - playerparticles.particles.max.2
* - playerparticles.particles.max.3
* - playerparticles.particles.max.4
* - playerparticles.particles.max.5
* - playerparticles.particles.max.6
* - playerparticles.particles.max.7
* - playerparticles.particles.max.8
* - playerparticles.particles.max.9
* - playerparticles.particles.max.10
* - playerparticles.particles.max.unlimited
* Note: The default max particles in the config.yml is used instead if the playerparticles.particles.max.# is lower
* Note: The highest number the user has permission for is how many they are able to use
* Ex. they have 4 and 7, they will have a max of 7
*/
package com.esophose.playerparticles;
@ -48,7 +68,6 @@ public class PlayerParticles extends JavaPlugin {
* Registers the tab completer and the event listeners
* Checks if the config needs to be updated to the new version
* Makes sure the database is accessible
* Updates the map and styleMap @see ParticleCreator
* Starts the particle spawning task
* Registers the command executor
* Checks for any updates if checking is enabled in the config

View file

@ -124,7 +124,7 @@ public class AddCommandModule implements CommandModule {
group.getParticles().add(newParticle);
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.COMMAND_ADD_PARTICLE_APPLIED, newParticle.getEffect().getName(), newParticle.getStyle().getName(), newParticle.getDataString());
LangManager.sendMessage(pplayer, Lang.ADD_PARTICLE_APPLIED, newParticle.getEffect().getName(), newParticle.getStyle().getName(), newParticle.getDataString());
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {

View file

@ -38,7 +38,7 @@ public class DataCommandModule implements CommandModule {
LangManager.sendMessage(pplayer, Lang.DATA_USAGE_NONE, effect.getName());
}
} else {
LangManager.sendMessage(pplayer, Lang.COMMAND_DATA_NO_ARGS);
LangManager.sendMessage(pplayer, Lang.DATA_NO_ARGS);
}
}

View file

@ -33,17 +33,17 @@ public class EditCommandModule implements CommandModule {
try {
id = Integer.parseInt(args[0]);
} catch (Exception e) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_INVALID);
LangManager.sendMessage(pplayer, Lang.ID_INVALID);
return;
}
if (id <= 0) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_INVALID);
LangManager.sendMessage(pplayer, Lang.ID_INVALID);
return;
}
if (pplayer.getActiveParticle(id) == null) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_UNKNOWN, id);
LangManager.sendMessage(pplayer, Lang.ID_UNKNOWN, id);
return;
}
@ -63,7 +63,7 @@ public class EditCommandModule implements CommandModule {
editData(pplayer, id, cmdArgs);
break;
default:
LangManager.sendMessage(pplayer, Lang.COMMAND_EDIT_INVALID_PROPERTY, args[1]);
LangManager.sendMessage(pplayer, Lang.EDIT_INVALID_PROPERTY, args[1]);
break;
}
}
@ -94,7 +94,7 @@ public class EditCommandModule implements CommandModule {
}
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.COMMAND_EDIT_SUCCESS_EFFECT, id, effect.getName());
LangManager.sendMessage(pplayer, Lang.EDIT_SUCCESS_EFFECT, id, effect.getName());
}
/**
@ -123,7 +123,7 @@ public class EditCommandModule implements CommandModule {
}
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.COMMAND_EDIT_SUCCESS_STYLE, id, style.getName());
LangManager.sendMessage(pplayer, Lang.EDIT_SUCCESS_STYLE, id, style.getName());
}
/**
@ -220,7 +220,7 @@ public class EditCommandModule implements CommandModule {
}
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.COMMAND_EDIT_SUCCESS_DATA, id, updatedDataString);
LangManager.sendMessage(pplayer, Lang.EDIT_SUCCESS_DATA, id, updatedDataString);
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {
@ -252,10 +252,12 @@ public class EditCommandModule implements CommandModule {
} else if (args.length >= 3) {
switch (args[1].toLowerCase()) {
case "effect":
StringUtil.copyPartialMatches(args[2], PermissionManager.getEffectsUserHasPermissionFor(p), matches);
if (args.length == 3)
StringUtil.copyPartialMatches(args[2], PermissionManager.getEffectsUserHasPermissionFor(p), matches);
break;
case "style":
StringUtil.copyPartialMatches(args[2], PermissionManager.getStylesUserHasPermissionFor(p), matches);
if (args.length == 3)
StringUtil.copyPartialMatches(args[2], PermissionManager.getStylesUserHasPermissionFor(p), matches);
break;
case "data":
ParticleEffect effect = pplayer.getActiveParticle(id).getEffect();

View file

@ -14,9 +14,9 @@ import org.bukkit.util.StringUtil;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.manager.ParticleManager;
import com.esophose.playerparticles.manager.PermissionManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.particles.FixedParticleEffect;
import com.esophose.playerparticles.particles.PPlayer;
import com.esophose.playerparticles.particles.ParticleEffect;
@ -25,7 +25,6 @@ import com.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import com.esophose.playerparticles.particles.ParticleEffect.ParticleProperty;
import com.esophose.playerparticles.particles.ParticlePair;
import com.esophose.playerparticles.styles.api.ParticleStyle;
import com.esophose.playerparticles.styles.api.ParticleStyleManager;
import com.esophose.playerparticles.util.ParticleUtils;
public class FixedCommandModule implements CommandModule {
@ -388,8 +387,7 @@ public class FixedCommandModule implements CommandModule {
String[] subCommands = new String[] { "create", "remove", "list", "info", "clear" };
if (args.length <= 1) {
List<String> possibleCmds = new ArrayList<String>();
possibleCmds.addAll(new ArrayList<String>(Arrays.asList(subCommands)));
List<String> possibleCmds = new ArrayList<String>(Arrays.asList(subCommands));
if (args.length == 0) matches = possibleCmds;
else StringUtil.copyPartialMatches(args[0], possibleCmds, matches);
} else {
@ -470,7 +468,7 @@ public class FixedCommandModule implements CommandModule {
}
public String getArguments() {
return "<args>";
return "<sub-command>";
}
public boolean requiresEffects() {

View file

@ -1,18 +1,230 @@
package com.esophose.playerparticles.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import com.esophose.playerparticles.manager.DataManager;
import com.esophose.playerparticles.manager.LangManager;
import com.esophose.playerparticles.manager.LangManager.Lang;
import com.esophose.playerparticles.particles.PPlayer;
import com.esophose.playerparticles.particles.ParticleGroup;
import com.esophose.playerparticles.particles.ParticlePair;
public class GroupCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
Player p = pplayer.getPlayer();
List<String> validCommands = Arrays.asList(new String[] { "save", "load", "remove", "info", "list" });
if (args.length == 0 || !validCommands.contains(args[0])) {
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_SAVE);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_LOAD);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_REMOVE);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_INFO);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_LIST);
return;
}
if (args.length == 1 && !args[0].equalsIgnoreCase("list")) {
LangManager.sendMessage(p, Lang.GROUP_NO_NAME, args[0].toLowerCase());
return;
}
switch (args[0].toLowerCase()) {
case "save":
onSave(pplayer, args[1].toLowerCase());
break;
case "load":
onLoad(pplayer, args[1].toLowerCase());
break;
case "remove":
onRemove(pplayer, args[1].toLowerCase());
break;
case "info":
onInfo(pplayer, args[1].toLowerCase());
break;
case "list":
onList(pplayer);
break;
default:
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_SAVE);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_LOAD);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_REMOVE);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_INFO);
LangManager.sendMessage(p, Lang.COMMAND_DESCRIPTION_GROUP_LIST);
break;
}
}
/**
* Handles the command /pp group save
*
* @param pplayer The PPlayer
* @param groupName The target group name
*/
private void onSave(PPlayer pplayer, String groupName) {
// Check that the groupName isn't the reserved name
if (groupName.equalsIgnoreCase(ParticleGroup.DEFAULT_NAME)) {
LangManager.sendMessage(pplayer, Lang.GROUP_RESERVED);
return;
}
// The database column can only hold up to 100 characters, cut it off there
if (groupName.length() >= 100) {
groupName = groupName.substring(0, 100);
}
// Use the existing group if available, otherwise create a new one
ParticleGroup group = pplayer.getParticlesByName(groupName);
boolean groupUpdated = false;
if (group == null) {
List<ParticlePair> particles = new ArrayList<ParticlePair>();
for (ParticlePair particle : pplayer.getActiveParticles())
particles.add(particle.clone()); // Make sure the ParticlePairs aren't the same references in both the active and saved group
group = new ParticleGroup(groupName, particles);
} else {
groupUpdated = true;
}
// Apply changes and notify player
DataManager.saveParticleGroup(pplayer.getUniqueId(), group);
if (groupUpdated) {
LangManager.sendMessage(pplayer, Lang.GROUP_SAVE_SUCCESS_OVERWRITE, groupName);
} else {
LangManager.sendMessage(pplayer, Lang.GROUP_SAVE_SUCCESS, groupName);
}
}
/**
* Handles the command /pp group load
*
* @param pplayer The PPlayer
* @param groupName The target group name
*/
private void onLoad(PPlayer pplayer, String groupName) {
// Check that the groupName isn't the reserved name
if (groupName.equalsIgnoreCase(ParticleGroup.DEFAULT_NAME)) {
LangManager.sendMessage(pplayer, Lang.GROUP_RESERVED);
return;
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
}
// Empty out the active group and fill it with clones from the target group
ParticleGroup activeGroup = pplayer.getActiveParticleGroup();
activeGroup.getParticles().clear();
for (ParticlePair particle : group.getParticles())
activeGroup.getParticles().add(particle.clone());
// Update group and notify player
DataManager.saveParticleGroup(pplayer.getUniqueId(), activeGroup);
LangManager.sendMessage(pplayer, Lang.GROUP_LOAD_SUCCESS, activeGroup.getParticles().size(), groupName);
}
/**
* Handles the command /pp group remove
*
* @param pplayer The PPlayer
* @param groupName The target group name
*/
private void onRemove(PPlayer pplayer, String groupName) {
// Check that the groupName isn't the reserved name
if (groupName.equalsIgnoreCase(ParticleGroup.DEFAULT_NAME)) {
LangManager.sendMessage(pplayer, Lang.GROUP_RESERVED);
return;
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
}
// Delete the group and notify player
DataManager.removeParticleGroup(pplayer.getUniqueId(), group);
LangManager.sendMessage(pplayer, Lang.GROUP_REMOVE_SUCCESS, groupName);
}
/**
* Handles the command /pp group info
*
* @param pplayer The PPlayer
* @param groupName The target group name
*/
private void onInfo(PPlayer pplayer, String groupName) {
// Check that the groupName isn't the reserved name
if (groupName.equalsIgnoreCase(ParticleGroup.DEFAULT_NAME)) {
LangManager.sendMessage(pplayer, Lang.GROUP_RESERVED);
return;
}
// Get the group
ParticleGroup group = pplayer.getParticlesByName(groupName);
if (group == null) {
LangManager.sendMessage(pplayer, Lang.GROUP_INVALID, groupName);
return;
}
LangManager.sendMessage(pplayer, Lang.GROUP_INFO_HEADER, groupName);
for (ParticlePair particle : group.getParticles())
LangManager.sendMessage(pplayer, Lang.LIST_OUTPUT, particle.getId(), particle.getEffect().getName(), particle.getStyle().getName(), particle.getDataString());
}
/**
* Handles the command /pp group list
*
* @param pplayer The PPlayer
*/
private void onList(PPlayer pplayer) {
List<ParticleGroup> groups = pplayer.getParticleGroups();
if (groups.size() == 1) {
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_NONE);
return;
}
String groupsList = "";
for (ParticleGroup group : groups)
if (!group.getName().equals(ParticleGroup.DEFAULT_NAME))
groupsList += group.getName() + ", ";
if (groupsList.endsWith(", "))
groupsList = groupsList.substring(0, groupsList.length() - 2);
LangManager.sendMessage(pplayer, Lang.GROUP_LIST_OUTPUT, groupsList);
// TODO: Implement Group Presets and output them here
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {
return null;
List<String> matches = new ArrayList<String>();
List<String> subCommands = Arrays.asList(new String[] { "save", "load", "remove", "info", "list" });
if (args.length <= 1) {
if (args.length == 0) matches = subCommands;
else StringUtil.copyPartialMatches(args[0], subCommands, matches);
} else if (args.length == 2 && !args[0].equalsIgnoreCase("list")) {
if (args[0].equalsIgnoreCase("save")) {
matches.add("<groupName>");
} else {
List<String> groupNames = new ArrayList<String>();
for (ParticleGroup group : pplayer.getParticleGroups())
if (!group.getName().equals(ParticleGroup.DEFAULT_NAME))
groupNames.add(group.getName());
// TODO: Include Group Presets and add them to groupNames
StringUtil.copyPartialMatches(args[1], groupNames, matches);
}
}
return matches;
}
public String getName() {
@ -24,7 +236,7 @@ public class GroupCommandModule implements CommandModule {
}
public String getArguments() {
return "<args>";
return "<sub-command>";
}
public boolean requiresEffects() {

View file

@ -14,17 +14,17 @@ public class ListCommandModule implements CommandModule {
List<ParticlePair> particles = pplayer.getActiveParticles();
if (particles.isEmpty()) {
LangManager.sendMessage(pplayer, Lang.COMMAND_LIST_NONE);
LangManager.sendMessage(pplayer, Lang.LIST_NONE);
return;
}
LangManager.sendMessage(pplayer, Lang.COMMAND_LIST_YOU_HAVE);
LangManager.sendMessage(pplayer, Lang.LIST_YOU_HAVE);
for (ParticlePair particle : particles) {
int id = particle.getId();
String effect = particle.getEffect().getName();
String style = particle.getStyle().getName();
String data = particle.getDataString();
LangManager.sendMessage(pplayer, Lang.COMMAND_LIST_OUTPUT, id, effect, style, data);
LangManager.sendMessage(pplayer, Lang.LIST_OUTPUT, id, effect, style, data);
}
}

View file

@ -136,7 +136,7 @@ public class ParticleCommandHandler implements CommandExecutor, TabCompleter {
}
}
}
return null;
return new ArrayList<String>();
}
}

View file

@ -16,7 +16,7 @@ public class RemoveCommandModule implements CommandModule {
public void onCommandExecute(PPlayer pplayer, String[] args) {
if (args.length == 0) {
LangManager.sendMessage(pplayer, Lang.COMMAND_REMOVE_NO_ARGS);
LangManager.sendMessage(pplayer, Lang.REMOVE_NO_ARGS);
return;
}
@ -24,12 +24,12 @@ public class RemoveCommandModule implements CommandModule {
try {
id = Integer.parseInt(args[0]);
} catch (Exception ex) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_INVALID);
LangManager.sendMessage(pplayer, Lang.ID_INVALID);
return;
}
if (id <= 0) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_INVALID);
LangManager.sendMessage(pplayer, Lang.ID_INVALID);
return;
}
@ -44,12 +44,12 @@ public class RemoveCommandModule implements CommandModule {
}
if (!removed) {
LangManager.sendMessage(pplayer, Lang.COMMAND_ID_UNKNOWN, id);
LangManager.sendMessage(pplayer, Lang.ID_UNKNOWN, id);
return;
}
DataManager.saveParticleGroup(pplayer.getUniqueId(), activeGroup);
LangManager.sendMessage(pplayer, Lang.COMMAND_REMOVE_SUCCESS, id);
LangManager.sendMessage(pplayer, Lang.REMOVE_SUCCESS, id);
}
public List<String> onTabComplete(PPlayer pplayer, String[] args) {

View file

@ -18,7 +18,6 @@ import com.esophose.playerparticles.particles.ParticleEffect.OrdinaryColor;
import com.esophose.playerparticles.particles.ParticleGroup;
import com.esophose.playerparticles.particles.ParticlePair;
import com.esophose.playerparticles.styles.api.ParticleStyle;
import com.esophose.playerparticles.styles.api.ParticleStyleManager;
import com.esophose.playerparticles.util.ParticleUtils;
/**
@ -247,13 +246,13 @@ public class DataManager {
});
getPPlayer(playerUUID, (pplayer) -> {
for (ParticleGroup existing : pplayer.getParticles()) {
for (ParticleGroup existing : pplayer.getParticleGroups()) {
if (group.getName().equalsIgnoreCase(existing.getName())) {
pplayer.getParticles().remove(existing);
pplayer.getParticleGroups().remove(existing);
break;
}
}
pplayer.getParticles().add(group);
pplayer.getParticleGroups().add(group);
});
}
@ -297,7 +296,7 @@ public class DataManager {
});
getPPlayer(playerUUID, (pplayer) -> {
pplayer.getParticles().remove(group);
pplayer.getParticleGroups().remove(group);
});
}

View file

@ -59,29 +59,44 @@ public class LangManager {
COMMAND_DESCRIPTION_GROUP_INFO,
// ID Lookup
COMMAND_ID_INVALID,
COMMAND_ID_UNKNOWN,
ID_INVALID,
ID_UNKNOWN,
// Add Command
COMMAND_ADD_PARTICLE_APPLIED,
ADD_PARTICLE_APPLIED,
// Data Command
COMMAND_DATA_NO_ARGS,
DATA_NO_ARGS,
// Edit Command
COMMAND_EDIT_INVALID_PROPERTY,
COMMAND_EDIT_SUCCESS_EFFECT,
COMMAND_EDIT_SUCCESS_STYLE,
COMMAND_EDIT_SUCCESS_DATA,
EDIT_INVALID_PROPERTY,
EDIT_SUCCESS_EFFECT,
EDIT_SUCCESS_STYLE,
EDIT_SUCCESS_DATA,
// Group Command
GROUP_INVALID,
GROUP_RESERVED,
GROUP_NO_NAME,
GROUP_SAVE_REACHED_MAX,
GROUP_SAVE_NO_PARTICLES,
GROUP_SAVE_SUCCESS,
GROUP_SAVE_SUCCESS_OVERWRITE,
GROUP_LOAD_SUCCESS,
GROUP_REMOVE_SUCCESS,
GROUP_INFO_HEADER,
GROUP_LIST_NONE,
GROUP_LIST_OUTPUT,
GROUP_LIST_PRESETS,
// Remove Command
COMMAND_REMOVE_NO_ARGS,
COMMAND_REMOVE_SUCCESS,
REMOVE_NO_ARGS,
REMOVE_SUCCESS,
// List Command
COMMAND_LIST_NONE,
COMMAND_LIST_YOU_HAVE,
COMMAND_LIST_OUTPUT,
LIST_NONE,
LIST_YOU_HAVE,
LIST_OUTPUT,
// Rainbow
RAINBOW,

View file

@ -63,7 +63,7 @@ public class PPlayer {
*
* @return A List of ParticleGroups this player has
*/
public List<ParticleGroup> getParticles() {
public List<ParticleGroup> getParticleGroups() {
return this.particleGroups;
}

View file

@ -241,6 +241,13 @@ public class ParticlePair {
}
return "none";
}
/**
* Gets a copy of this ParticlePair
*/
public ParticlePair clone() {
return new ParticlePair(this.ownerUUID, this.id, this.effect, this.style, this.itemMaterial, this.blockMaterial, this.color, this.noteColor);
}
/**
* Gets a ParticlePair with the default values applied

View file

@ -20,7 +20,7 @@ command-description-edit: 'Edit the effect, style, or data of an active particle
command-description-effect: '&cThis command has been removed, use &b/pp help &cto find new commands'
command-description-effects: 'Display a list of effects you can use'
command-description-fixed: 'Create, edit, and remove fixed particle effects'
command-description-group: 'Save, load, and remove your particle groups'
command-description-group: 'Save, load, and remove particle groups'
command-description-gui: 'Display the GUI for easy editing of particles'
command-description-help: 'Displays the help menu... You have arrived'
command-description-info: 'Gets the description of one of your active particles'
@ -45,29 +45,44 @@ command-description-group-list: '&e/pp group list <name> - List all particle gro
command-description-group-info: '&e/pp group info <name> - List the particles saved in the group'
# Command ID Lookup
command-id-invalid: '&cThe ID you entered is invalid, it must be a positive whole number!'
command-id-unknown: '&cYou do not have a particle applied with the ID &b{0}&c!'
id-invalid: '&cThe ID you entered is invalid, it must be a positive whole number!'
id-unknown: '&cYou do not have a particle applied with the ID &b{0}&c!'
# Add Command
command-add-particle-applied: '&aA new particle has been applied with the effect &b{0}&a, style &b{1}&a, and data &b{2}&a!'
add-particle-applied: '&aA new particle has been applied with the effect &b{0}&a, style &b{1}&a, and data &b{2}&a!'
# Data Command
command-data-no-args: '&cMissing argument for effect! Command usage: &b/pp data <effect>'
data-no-args: '&cMissing argument for effect! Command usage: &b/pp data <effect>'
# Edit Command
command-edit-invalid-property: '&cAn invalid property &b{0} &cwas provided. Valid properties: &beffect&c, &bstyle&c, &bdata'
command-edit-success-effect: '&aYour particle with an ID of &b{0} &ahas had its effect changed to &b{1}&a!'
command-edit-success-style: '&aYour particle with an ID of &b{0} &ahas had its style changed to &b{1}&a!'
command-edit-success-data: '&aYour particle with an ID of &b{0} &ahas had its data changed to &b{1}&a!'
edit-invalid-property: '&cAn invalid property &b{0} &cwas provided. Valid properties: &beffect&c, &bstyle&c, &bdata'
edit-success-effect: '&aYour particle with an ID of &b{0} &ahas had its effect changed to &b{1}&a!'
edit-success-style: '&aYour particle with an ID of &b{0} &ahas had its style changed to &b{1}&a!'
edit-success-data: '&aYour particle with an ID of &b{0} &ahas had its data changed to &b{1}&a!'
# Group Command
group-invalid: '&cA group does not exist with the name &b{0}&c!'
group-reserved: '&cThe group name &bactive &cis reserved and cannot be used!'
group-no-name: '&cYou did not provide a group name! &b/pp {0} <groupName>'
group-save-reached-max: '&cUnable to save group, you have reached the max number of groups!'
group-save-no-particles: '&cUnable to save group, you do not have any particles applied!'
group-save-success: '&aYour current particles have been saved under the group named &b{0}&a!'
group-save-success-overwrite: '&aThe group named &b{0} &ahas been updated with your current particles!'
group-load-success: '&aApplied &b{0} &aparticles from the group &b{1}&a!'
group-remove-success: '&aRemoved the particle group named &b{0}&a!'
group-info-header: '&eThe group &b{0} &ehas the following particles:'
group-list-none: '&eYou do not have any particle groups saved!'
group-list-output: '&eYou have the following groups saved: &b{0}'
group-list-presets: '&eThe following preset groups are available: &b{0}'
# Remove Command
command-remove-no-args: '&cYou did not specify an ID to remove! &b/pp remove <ID>'
command-remove-success: '&aYour particle with the ID &b{0} &ahas been removed!'
remove-no-args: '&cYou did not specify an ID to remove! &b/pp remove <ID>'
remove-success: '&aYour particle with the ID &b{0} &ahas been removed!'
# List Command
command-list-none: '&eYou do not have any active particles!'
command-list-you-have: '&eYou have the following particles applied:'
command-list-output: '&eID: &b{0} &eEffect: &b{1} &eStyle: &b{2} &eData: &b{3}'
list-none: '&eYou do not have any active particles!'
list-you-have: '&eYou have the following particles applied:'
list-output: '&eID: &b{0} &eEffect: &b{1} &eStyle: &b{2} &eData: &b{3}'
# Rainbow
rainbow: '&cr&6a&ei&an&bb&9o&dw'