diff --git a/src/main/java/ch/njol/skript/expressions/ExprBeaconEffects.java b/src/main/java/ch/njol/skript/expressions/ExprBeaconEffects.java new file mode 100644 index 00000000000..1cdd6873b0b --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprBeaconEffects.java @@ -0,0 +1,103 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.*; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; +import io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent; +import org.bukkit.block.Beacon; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +@Name("Beacon Effects") +@Description({ + "The active effects of a beacon.", + "The secondary effect can be set to anything, but the icon in the GUI will not display correctly.", + "The secondary effect can only be set when the beacon is at max tier.", + "The primary and secondary effect can not be the same, primary will always retain the potion type and secondary will be cleared." +}) +@Example(""" + set primary beacon effect of {_block} to haste + set secondary effect of {_block} to resistance + """ +) +@Events({"Beacon Effect", "Beacon Toggle", "Beacon Change Effect"}) +@Since("2.10") +public class ExprBeaconEffects extends PropertyExpression { + + private static final boolean SUPPORTS_CHANGE_EVENT = Skript.classExists("io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent"); + + static { + registerDefault(ExprBeaconEffects.class, PotionEffectType.class, "(:primary|secondary) [beacon] effect", "blocks"); + } + + private boolean primary; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + setExpr((Expression) expressions[0]); + primary = parseResult.hasTag("primary"); + return true; + } + + @Override + protected PotionEffectType[] get(Event event, Block[] source) { + return get(source, block -> { + if (!(block.getState() instanceof Beacon beacon)) + return null; + + if (SUPPORTS_CHANGE_EVENT + && event instanceof PlayerChangeBeaconEffectEvent changeEvent + && block.equals(changeEvent.getBeacon())) + return primary ? changeEvent.getPrimary() : changeEvent.getSecondary(); + + PotionEffect effect = primary ? beacon.getPrimaryEffect() : beacon.getSecondaryEffect(); + if (effect == null) + return null; + return effect.getType(); + }); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, RESET, DELETE -> CollectionUtils.array(PotionEffectType.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + PotionEffectType type = delta == null ? null : (PotionEffectType) delta[0]; + BiConsumer changer = primary ? Beacon::setPrimaryEffect : Beacon::setSecondaryEffect; + for (Block block : getExpr().getArray(event)) { + if (!(block.getState() instanceof Beacon beacon)) + continue; + changer.accept(beacon, type); + beacon.update(true); + } + } + + @Override + public Class getReturnType() { + return PotionEffectType.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (primary) + return "primary beacon effect of " + getExpr().toString(event, debug); + return "secondary beacon effect of " + getExpr().toString(event, debug); + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprBeaconRange.java b/src/main/java/ch/njol/skript/expressions/ExprBeaconRange.java new file mode 100644 index 00000000000..c35d355f68f --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprBeaconRange.java @@ -0,0 +1,86 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.*; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.block.Beacon; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Beacon Range") +@Description({ + "The range of a beacon's effects, in blocks." +}) +@Example(""" + if the beacon tier of the clicked block is 4: + set the beacon effect range of the clicked block to 100 + """ +) +@RequiredPlugins("Paper") +@Since("2.10") +public class ExprBeaconRange extends SimplePropertyExpression { + + private static final boolean SUPPORTS_RANGE = Skript.methodExists(Beacon.class, "getEffectRange"); + + static { + if (SUPPORTS_RANGE) + register(ExprBeaconRange.class, Double.class, "beacon [effect] range", "blocks"); + } + + @Override + public @Nullable Double convert(Block from) { + if (from.getState() instanceof Beacon beacon) + return beacon.getEffectRange(); + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, ADD, REMOVE, RESET -> CollectionUtils.array(Double.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + if (mode == ChangeMode.RESET) { + for (Block block : getExpr().getArray(event)) { + if (block.getState() instanceof Beacon beacon) { + beacon.resetEffectRange(); + beacon.update(true); + } + } + return; + } + + assert delta != null; + double range = ((Number) delta[0]).doubleValue(); + for (Block block : getExpr().getArray(event)) { + if (block.getState() instanceof Beacon beacon) { + switch (mode) { + case SET -> beacon.setEffectRange(Math2.fit(0, range, Double.MAX_VALUE)); + case ADD -> beacon.setEffectRange(Math2.fit(0, beacon.getEffectRange() + range, Double.MAX_VALUE)); + case REMOVE -> beacon.setEffectRange(Math2.fit(0, beacon.getEffectRange() - range, Double.MAX_VALUE)); + default -> throw new IllegalStateException(); + } + beacon.update(true); + } + } + } + + @Override + public Class getReturnType() { + return Double.class; + } + + @Override + protected String getPropertyName() { + return "beacon range"; + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprBeaconTier.java b/src/main/java/ch/njol/skript/expressions/ExprBeaconTier.java new file mode 100644 index 00000000000..448369327bf --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprBeaconTier.java @@ -0,0 +1,45 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import org.bukkit.block.Beacon; +import org.bukkit.block.Block; +import org.jetbrains.annotations.Nullable; + +@Name("Beacon Tier") +@Description({ + "The tier of a beacon. Ranges from 0 to 4." +}) +@Example(""" + if the beacon tier of the clicked block is 4: + send "This is a max tier beacon!" + """ +) +@Since("2.10") +public class ExprBeaconTier extends SimplePropertyExpression { + + static { + register(ExprBeaconTier.class, Integer.class, "beacon tier", "blocks"); + } + + @Override + public @Nullable Integer convert(Block from) { + if (from.getState() instanceof Beacon beacon) + return beacon.getTier(); + return null; + } + + @Override + public Class getReturnType() { + return Integer.class; + } + + @Override + protected String getPropertyName() { + return "beacon tier"; + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprBeaconValues.java b/src/main/java/ch/njol/skript/expressions/ExprBeaconValues.java deleted file mode 100644 index 887a0a0cc56..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprBeaconValues.java +++ /dev/null @@ -1,206 +0,0 @@ -package ch.njol.skript.expressions; - -import ch.njol.skript.Skript; -import ch.njol.skript.classes.Changer.ChangeMode; -import ch.njol.skript.doc.*; -import ch.njol.skript.expressions.base.EventValueExpression; -import ch.njol.skript.expressions.base.PropertyExpression; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; -import ch.njol.util.Math2; -import ch.njol.util.coll.CollectionUtils; -import com.destroystokyo.paper.event.block.BeaconEffectEvent; -import io.papermc.paper.event.block.BeaconActivatedEvent; -import io.papermc.paper.event.block.BeaconDeactivatedEvent; -import io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent; -import org.bukkit.block.Beacon; -import org.bukkit.block.Block; -import org.bukkit.event.Event; -import org.bukkit.potion.PotionEffectType; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Consumer; - -@Name("Beacon Effects") -@Description({ - "The active effects of a beacon.", - "The secondary effect can be set to anything, but the icon in the GUI will not display correctly.", - "The secondary effect can only be set when the beacon is at max tier.", - "The primary and secondary effect can not be the same, primary will always retain the potion type and secondary will be cleared.", - "You can only change the range on Paper." -}) -@Examples({ - "broadcast tier of {_block}", - "set primary beacon effect of {_block} to haste", - "add 1 to range of {_block}" -}) -@RequiredPlugins("Paper (range)") -@Events({"Beacon Effect", "Beacon Toggle", "Beacon Change Effect"}) -@Since("2.10") -public class ExprBeaconValues extends PropertyExpression { - - enum BeaconValues { - PRIMARY("primary [beacon] effect"), - SECONDARY("secondary [beacon] effect"), - RANGE("[beacon] range"), - TIER("[beacon] tier"); - - private final String pattern; - - BeaconValues(String pattern) { - this.pattern = pattern; - } - } - - private static final boolean SUPPORTS_CHANGE_EVENT = Skript.classExists("io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent"); - private static final boolean SUPPORTS_RANGE = Skript.methodExists(Beacon.class, "getEffectRange"); - private static BeaconValues[] beaconValues = BeaconValues.values(); - - static { - String patternEnding = " of %blocks%"; - if (SUPPORTS_CHANGE_EVENT) - patternEnding = " [of %blocks%]"; - int size = beaconValues.length; - String[] patterns = new String[size * 2]; - for (BeaconValues value : beaconValues) { - patterns[2 * value.ordinal()] = "%blocks%['s] " + value.pattern; - patterns[2 * value.ordinal() + 1] = "[the] " + value.pattern + patternEnding; - } - - Skript.registerExpression(ExprBeaconValues.class, Object.class, ExpressionType.PROPERTY, patterns); - } - - private BeaconValues valueType; - private boolean isEffect; - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - valueType = beaconValues[(int) Math2.floor(matchedPattern/2)]; - if (valueType == BeaconValues.RANGE && !SUPPORTS_RANGE) { - Skript.error(valueType.pattern + " can only be used on Paper."); - return false; - } - if (exprs[0] != null) { - setExpr((Expression) exprs[0]); - } else { - if (!getParser().isCurrentEvent(PlayerChangeBeaconEffectEvent.class, BeaconEffectEvent.class, BeaconActivatedEvent.class, BeaconDeactivatedEvent.class)) { - Skript.error("There is no beacon in a " + getParser().getCurrentEventName() + " event."); - return false; - } - setExpr(new EventValueExpression<>(Block.class)); - } - if (valueType == BeaconValues.PRIMARY || valueType == BeaconValues.SECONDARY) - isEffect = true; - - return true; - } - - @Override - protected Object @Nullable [] get(Event event, Block[] source) { - return get(source, block -> { - Beacon beacon = (Beacon) block.getState(); - return switch (valueType) { - case PRIMARY -> { - if (SUPPORTS_CHANGE_EVENT && event instanceof PlayerChangeBeaconEffectEvent changeEvent) { - yield changeEvent.getPrimary(); - } else if (beacon.getPrimaryEffect() != null) { - yield beacon.getPrimaryEffect().getType(); - } - yield null; - } - case SECONDARY-> { - if (SUPPORTS_CHANGE_EVENT && event instanceof PlayerChangeBeaconEffectEvent changeEvent) { - yield changeEvent.getSecondary(); - } else if (beacon.getSecondaryEffect() != null) { - yield beacon.getSecondaryEffect().getType(); - } - yield null; - } - case RANGE -> beacon.getEffectRange(); - case TIER -> beacon.getTier(); - }; - }); - } - - @Override - public Class @Nullable [] acceptChange(ChangeMode mode) { - if (mode != ChangeMode.REMOVE_ALL && valueType != BeaconValues.TIER) { - if (!isEffect || (isEffect && (mode != ChangeMode.ADD && mode != ChangeMode.REMOVE))) { - return CollectionUtils.array(Object.class); - } - } - return null; - } - - @Override - public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - PotionEffectType providedEffect = null; - double providedRange = 0; - if (delta != null && delta[0] != null) { - if (isEffect && delta[0] instanceof PotionEffectType potionEffectType) { - providedEffect = potionEffectType; - } else if (valueType == BeaconValues.RANGE) { - providedRange = (double) ((long) delta[0]); - } - } - switch (valueType) { - case RANGE -> changeRange(event, providedRange, mode); - case PRIMARY -> changePrimary(event, providedEffect, mode); - case SECONDARY -> changeSecondary(event, providedEffect, mode); - } - } - - private void changeBeacons(Event event, Consumer changer) { - for (Block block : getExpr().getArray(event)) { - Beacon beacon = (Beacon) block.getState(); - changer.accept(beacon); - beacon.update(true); - } - } - - private void changeRange(Event event, double providedRange, ChangeMode mode) { - switch (mode) { - case ADD -> changeBeacons(event, beacon -> beacon.setEffectRange(Math2.fit(0, beacon.getEffectRange() + providedRange, Integer.MAX_VALUE))); - case REMOVE -> changeBeacons(event, beacon -> beacon.setEffectRange(Math2.fit(0, beacon.getEffectRange() - providedRange, Integer.MAX_VALUE))); - case SET -> changeBeacons(event, beacon -> beacon.setEffectRange(Math2.fit(0, providedRange, Integer.MAX_VALUE))); - case DELETE, RESET -> changeBeacons(event, Beacon::resetEffectRange); - } - } - - private void changePrimary(Event event, PotionEffectType providedEffect, ChangeMode mode) { - switch (mode) { - case SET -> changeBeacons(event, beacon -> beacon.setPrimaryEffect(providedEffect)); - case DELETE, RESET -> changeBeacons(event, beacon -> beacon.setPrimaryEffect(null)); - } - } - - private void changeSecondary(Event event, PotionEffectType providedEffect, ChangeMode mode) { - switch (mode) { - case SET -> changeBeacons(event, beacon -> beacon.setSecondaryEffect(providedEffect)); - case DELETE, RESET -> changeBeacons(event, beacon -> beacon.setSecondaryEffect(null)); - } - } - - @Override - public boolean isSingle() { - return getExpr().isSingle(); - } - - @Override - public Class getReturnType() { - return Object.class; - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return switch (valueType) { - case PRIMARY -> "primary beacon effect"; - case SECONDARY -> "secondary beacon effect"; - case RANGE -> "beacon range"; - case TIER -> "beacon tier"; - } + " of " + getExpr().toString(event, debug); - } - -} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprBeaconValues.sk b/src/test/skript/tests/syntaxes/expressions/ExprBeaconValues.sk index 5928dc8cebe..e39e4bc9337 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprBeaconValues.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprBeaconValues.sk @@ -10,9 +10,15 @@ on load: assert primary beacon effect of {_block} is haste with "Set beacon primary effect to haste" clear primary beacon effect of {_block} assert primary beacon effect of {_block} is not set with "Clear beacon primary effect" - set range of {_block} to 10 - assert range of {_block} is 10 with "Set beacon range to 10" - add 5 to range of {_block} - assert range of {_block} is 15 with "Add 5 to beacon range" + set beacon range of {_block} to 10 + assert beacon range of {_block} is 10 with "Set beacon range to 10" + add 5 to beacon range of {_block} + assert beacon range of {_block} is 15 with "Add 5 to beacon range" set block at {_loc} to air + set primary beacon effect of {_block} to haste + assert primary beacon effect of {_block} is not set with "air block should not have beacon effect" + set beacon range of {_block} to 10 + assert beacon range of {_block} is not set with "air block should not have beacon range" + add 5 to beacon range of {_block} + assert beacon range of {_block} is not set with "air block should not have beacon range"