Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework EffectLib Integration #384

Merged
merged 1 commit into from
Aug 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@
<dependency>
<groupId>de.slikey</groupId>
<artifactId>EffectLib</artifactId>
<version>5.8</version>
<version>9.4</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/EffectLib-5.8.jar</systemPath>
<systemPath>${project.basedir}/lib/EffectLib.jar</systemPath>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.denizenscript.depenizen.bukkit.bridges;

import com.denizenscript.denizencore.DenizenCore;
import com.denizenscript.depenizen.bukkit.Depenizen;
import com.denizenscript.depenizen.bukkit.commands.effectlib.EffectLibCommand;
import com.denizenscript.depenizen.bukkit.Bridge;
import de.slikey.effectlib.EffectManager;

public class EffectLibBridge extends Bridge {

public static EffectLibBridge instance;
public EffectManager effectManager;

@Override
public void init() {
instance = this;
effectManager = new EffectManager(Depenizen.instance);
DenizenCore.commandRegistry.registerCommand(EffectLibCommand.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,170 +1,167 @@
package com.denizenscript.depenizen.bukkit.commands.effectlib;

import com.denizenscript.denizencore.objects.Argument;
import com.denizenscript.depenizen.bukkit.bridges.EffectLibBridge;
import de.slikey.effectlib.EffectManager;
import de.slikey.effectlib.effect.ArcEffect;
import de.slikey.effectlib.effect.AtomEffect;
import de.slikey.effectlib.effect.BleedEffect;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.objects.LocationTag;
import com.denizenscript.denizen.objects.PlayerTag;
import com.denizenscript.denizen.utilities.Utilities;
import com.denizenscript.denizencore.exceptions.InvalidArgumentsException;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.DurationTag;
import com.denizenscript.denizencore.objects.core.MapTag;
import com.denizenscript.denizencore.scripts.ScriptEntry;
import com.denizenscript.denizencore.scripts.commands.AbstractCommand;
import com.denizenscript.denizencore.scripts.commands.Holdable;
import com.denizenscript.denizencore.scripts.commands.generator.ArgDefaultNull;
import com.denizenscript.denizencore.scripts.commands.generator.ArgName;
import com.denizenscript.denizencore.scripts.commands.generator.ArgPrefixed;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import org.bukkit.Location;
import com.denizenscript.denizencore.utilities.debugging.SlowWarning;
import com.denizenscript.denizencore.utilities.debugging.Warning;
import com.denizenscript.denizencore.utilities.text.StringHolder;
import com.denizenscript.depenizen.bukkit.bridges.EffectLibBridge;
import de.slikey.effectlib.Effect;
import de.slikey.effectlib.util.DynamicLocation;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;

public class EffectLibCommand extends AbstractCommand {
import java.util.Map;

public class EffectLibCommand extends AbstractCommand implements Holdable {

public EffectLibCommand() {
setName("effectlib");
setSyntax("effectlib (type:<effect name>) (duration:<duration>) (target:<entity>/location:<location>)");
setRequiredArguments(1, 3);
setSyntax("effectlib [type:<effect>] (origin:<entity>/<location>) (target:<entity>/<location>) (for:<player>) (effect_data:<map>)");
setRequiredArguments(1, 5);
autoCompile();
}

// <--[command]
// @Name effectlib
// @Syntax effectlib (type:<effect name>) (duration:<duration>) (target:<entity>/location:<location>)
// @Syntax effectlib [type:<effect>] (origin:<entity>/<location>) (target:<entity>/<location>) (for:<player>) (effect_data:<map>)
// @Group Depenizen
// @Plugin Depenizen, EffectLib
// @Required 1
// @Maximum 3
// @Maximum 5
// @Short Show custom effects using EffectLib
//
// @Description
// Use to show custom effects in a easier way using the config file in EffectLib.
// The effect names comes from the config file.
// Specify a location instead of a target to show the effect at a location instead.
// Displays EffectLib effects.
// Specify which effect to display using the 'type:' argument, note that effect names are CASE-SENSITIVE.
// The origin is the entity/location to play the effect at. If an entity is specified, the effect will follow it.
// Defaults to the linked player if unspecified.
// Some effects (such as the 'Line' effect) require an origin and a target, an effect's target can be a location or an entity.
// If an entity is specified, the effect will follow it.
//
// You can optionally use the 'for:' argument to show the effect to one player only.
//
// EffectLib effects can take additional data to change how the effect looks/works, you can specify that data in the form of a MapTag using the 'effect_data:' argument.
// See EffectLib's docs for further information on the available options for each effect: <@link url https://reference.elmakers.com/#effectlib>
//
// Note that this should only be used for displaying effects. for showing single particles, etc. use <@link command playeffect>.
//
// Note that most users should instead use <@link command playeffect>.
// The effectlib command is ~waitable. Refer to <@link language ~waitable>.
// When ~waited for, the command will wait until the effect is done playing.
//
// @Tags
// None
//
// @Usage
// Use to show a effect on the attached player in queue.
// - effectlib bleed duration:10s
// Use to show a helix of end rod sparks below the linked player for 5 seconds.
// - effectlib type:Helix effect_data:[duration=<duration[5s].in_milliseconds>;particle=end_rod;offset=0,-1.5,0]
//
// @Usage
// Use to show a effect on a target entity.
// - effectlib atom target:<player.target> duration:10s
// Use to show a line of electric sparks between 2 entities for 1 minute.
// - effectlib type:Line origin:<[originEntity]> target:<[targetEntity]> effect_data:[duration=<duration[1m].in_milliseconds>;particle=electric_spark]
//
// @Usage
// Use to show effect at a position of the player.
// - effectlib type:atom duration:10s location:<player.location>
// Use to show an atom effect at a location, and narrate a message after it's done playing.
// - ~effectlib type:Atom origin:<[location]>
// - narrate "It's done!"
//
// -->

private enum Action {
BLEED, ARC, ATOM
}
public static Warning durationArgument = new SlowWarning("effectlibCommandDuration", "The 'duration:<duration>' argument for the 'effectlib' command is deprecated in favour of the 'duration' key in the 'effect_data' map, refer to the meta docs for more information.");
public static Warning oldTargetArguments = new SlowWarning("effectlibCommandOldTarget", "The 'target:<entity>'/'location:<location>' arguments for the 'effectlib' command are deprecated in favour of the 'origin:<entity>/<location>' argument.");
public static Warning caseInsensitiveEffectName = new SlowWarning("effectlibCommandCaseInsensitiveType", "Case-Insensitive effect types in the 'effectlib' commands are deprecated. make sure you're using correct casing.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels weird for this to be a deprecation - should probably be the other way around if anything - aim to support case-insensitivity better rather than plan its removal.


@Override
public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException {
for (Argument arg : scriptEntry) {
if (!scriptEntry.hasObject("target")
&& arg.matchesPrefix("target")) {
scriptEntry.addObject("target", arg.asType(EntityTag.class));
}
else if (!scriptEntry.hasObject("duration")
&& arg.matchesPrefix("duration")) {
scriptEntry.addObject("duration", arg.asType(DurationTag.class));
}
else if (!scriptEntry.hasObject("location")
&& arg.matchesPrefix("location")) {
scriptEntry.addObject("location", arg.asType(LocationTag.class));
}
else if (!scriptEntry.hasObject("action")
&& arg.matchesEnum(Action.class)) {
scriptEntry.addObject("action", Action.valueOf(arg.getValue().toUpperCase()));
}
else {
arg.reportUnhandled();
}
public static void autoExecute(ScriptEntry scriptEntry,
@ArgPrefixed @ArgName("type") String effectInput,
@ArgDefaultNull @ArgPrefixed @ArgName("origin") ObjectTag originInput,
@ArgDefaultNull @ArgPrefixed @ArgName("target") ObjectTag targetInput,
@ArgDefaultNull @ArgPrefixed @ArgName("for") PlayerTag forPlayer,
@ArgDefaultNull @ArgPrefixed @ArgName("effect_data") MapTag effectData,
@ArgDefaultNull @ArgPrefixed @ArgName("duration") DurationTag duration,
@ArgDefaultNull @ArgPrefixed @ArgName("location") LocationTag oldLocation) {
if (originInput == null && oldLocation != null) {
oldTargetArguments.warn(scriptEntry);
originInput = oldLocation;
targetInput = null;
}
if (originInput == null && targetInput != null && targetInput.shouldBeType(EntityTag.class)) {
oldTargetArguments.warn(scriptEntry);
originInput = targetInput;
targetInput = null;
}
if (!scriptEntry.hasObject("action")) {
throw new InvalidArgumentsException("Effect not specified!");
if (originInput == null && Utilities.entryHasPlayer(scriptEntry)) {
originInput = Utilities.getEntryPlayer(scriptEntry);
}
if (!scriptEntry.hasObject("duration")) {
throw new InvalidArgumentsException("Duration not specified!");
if (originInput == null) {
Debug.echoError(scriptEntry, "Must specify an origin location or entity!");
return;
}
// EffectLib is case-sensitive, and the way the command used to handle effect names let you input 'atom', where now 'Atom' is required
if (Character.isLowerCase(effectInput.charAt(0))) {
caseInsensitiveEffectName.warn(scriptEntry);
effectInput = Character.toUpperCase(effectInput.charAt(0)) + effectInput.substring(1);
}
if (!scriptEntry.hasObject("target")) {
if (Utilities.entryHasPlayer(scriptEntry)) {
scriptEntry.addObject("target", Utilities.getEntryPlayer(scriptEntry).getDenizenEntity());
Effect effect;
if (effectData == null) {
effect = EffectLibBridge.instance.effectManager.getEffectByClassName(effectInput);
if (effect != null) {
effect.setDynamicOrigin(getEffectLibLocationFrom(originInput, scriptEntry.context));
effect.setDynamicTarget(getEffectLibLocationFrom(targetInput, scriptEntry.context));
effect.setTargetPlayer(forPlayer != null ? forPlayer.getPlayerEntity() : null);
}
}

}

@Override
public void execute(final ScriptEntry scriptEntry) {
EntityTag target = scriptEntry.getObjectTag("target");
Action action = (Action) scriptEntry.getObject("action");
DurationTag duration = scriptEntry.getObjectTag("duration");
LocationTag location = scriptEntry.getObjectTag("location");
if (scriptEntry.dbCallShouldDebug()) {
Debug.report(scriptEntry, getName(), target, action, duration, location);
else {
ConfigurationSection effectLibData = new MemoryConfiguration();
for (Map.Entry<StringHolder, ObjectTag> entry : effectData.map.entrySet()) {
effectLibData.set(entry.getKey().str, CoreUtilities.objectTagToJavaForm(entry.getValue(), false, true));
}
effect = EffectLibBridge.instance.effectManager.getEffect(
effectInput,
effectLibData,
getEffectLibLocationFrom(originInput, scriptEntry.context),
getEffectLibLocationFrom(targetInput, scriptEntry.context),
null,
forPlayer != null ? forPlayer.getPlayerEntity() : null);
}
if (target == null && location == null) {
Debug.echoError(scriptEntry, "Target not found!");
if (effect == null) {
Debug.echoError(scriptEntry, "Invalid effect specified: " + effectInput);
return;
}
if (action == null) {
Debug.echoError(scriptEntry, "Effect type not specified!");
return;
effect.callback = () -> scriptEntry.setFinished(true);
if (duration != null) {
durationArgument.warn(scriptEntry);
effect.iterations = duration.getTicksAsInt();
}
if (duration == null) {
Debug.echoError(scriptEntry, "Duration not specified!");
return;
effect.start();
}

public static DynamicLocation getEffectLibLocationFrom(ObjectTag object, TagContext context) {
if (object == null) {
return null;
}
int ticks = duration.getTicksAsInt();
EffectManager effectManager = new EffectManager(EffectLibBridge.instance.plugin);
// TODO: Find a better way to handle all effects and add custom ones as well
switch (action) {
case BLEED: {
BleedEffect effect = new BleedEffect(effectManager);
if (location == null) {
effect.setEntity(target.getBukkitEntity());
}
else {
effect.setLocation(new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()));
}
effect.callback = () -> scriptEntry.setFinished(true);
effect.iterations = ticks;
effect.start();
return;
}
case ARC: {
ArcEffect effect = new ArcEffect(effectManager);
if (location == null) {
effect.setEntity(target.getBukkitEntity());
}
else {
effect.setLocation(new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()));
}
effect.callback = () -> scriptEntry.setFinished(true);
effect.iterations = ticks;
effect.start();
return;
}
case ATOM: {
AtomEffect effect = new AtomEffect(effectManager);
if (location == null) {
effect.setEntity(target.getBukkitEntity());
}
else {
effect.setLocation(new Location(location.getWorld(), location.getX(), location.getY(), location.getZ()));
}
effect.callback = () -> scriptEntry.setFinished(true);
effect.iterations = ticks;
effect.start();
return;
}
default: {
Debug.echoError(scriptEntry, "Effect type not found!");
if (object.shouldBeType(LocationTag.class)) {
return new DynamicLocation(object.asType(LocationTag.class, context));
}
else {
EntityTag entity = object.asType(EntityTag.class, context);
if (entity == null) {
Debug.echoError(context, "Invalid input specified '" + object + "': must be a valid location or entity.");
return null;
}
return new DynamicLocation(entity.getBukkitEntity());
}
}
}