diff --git a/build.gradle b/build.gradle index 6052dc530da..2b24d2f48b8 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,15 @@ dependencies { shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.2' implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.21-R0.1-SNAPSHOT' + implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700' + + // Comes from Minecraft including Guava or Gson implementation group: 'com.google.code.findbugs', name: 'findbugs', version: '3.0.1' + + // bundled with Minecraft 1.19.4+ for display entity transforms + implementation group: 'org.joml', name: 'joml', version: '1.10.5' + + // Plugin hook libraries implementation group: 'com.sk89q.worldguard', name: 'worldguard-legacy', version: '7.0.0-SNAPSHOT' implementation group: 'net.milkbowl.vault', name: 'Vault', version: '1.7.3', { exclude group: 'org.bstats', module: 'bstats-bukkit' diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index c51112be9ac..dc4c742ce58 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -97,6 +97,7 @@ import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.skriptlang.skript.bukkit.SkriptMetrics; +import org.skriptlang.skript.bukkit.displays.DisplayModule; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; import org.skriptlang.skript.lang.converter.Converter; @@ -541,6 +542,9 @@ public void onEnable() { try { getAddonInstance().loadClasses("ch.njol.skript", "conditions", "effects", "events", "expressions", "entity", "sections", "structures"); + getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc"); + // todo: become proper module once registry api is merged + DisplayModule.load(); } catch (final Exception e) { exception(e, "Could not load required .class files: " + e.getLocalizedMessage()); setEnabled(false); diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index aa772d2b646..39ce6d21962 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -1414,6 +1414,22 @@ public Material getMaterial() { return data.getType(); } + /** + * @return A random block material this ItemType represents. + * @throws IllegalStateException If {@link #hasBlock()} is false. + */ + public Material getBlockMaterial() { + List blockItemDatas = new ArrayList<>(); + for (ItemData d : types) { + if (d.type.isBlock()) + blockItemDatas.add(d); + } + if (blockItemDatas.isEmpty()) + throw new IllegalStateException("This ItemType does not represent a material. " + + "ItemType#hasBlock() should return true before invoking this method."); + return blockItemDatas.get(random.nextInt(blockItemDatas.size())).getType(); + } + /** * Returns a base item type of this. Essentially, this calls * {@link ItemData#aliasCopy()} on all datas and creates a new type @@ -1427,4 +1443,5 @@ public ItemType getBaseType() { } return copy; } + } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 94b5fe789c4..c0271da3ae7 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -18,18 +18,32 @@ */ package ch.njol.skript.classes.data; -import java.io.StreamCorruptedException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - +import ch.njol.skript.Skript; +import ch.njol.skript.SkriptConfig; +import ch.njol.skript.aliases.Aliases; +import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.BukkitUtils; +import ch.njol.skript.bukkitutil.EnchantmentUtils; +import ch.njol.skript.bukkitutil.ItemUtils; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.ConfigurationSerializer; +import ch.njol.skript.classes.EnumClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; +import ch.njol.skript.classes.registry.RegistryClassInfo; +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.expressions.ExprDamageCause; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.lang.util.SimpleLiteral; +import ch.njol.skript.localization.Language; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.BlockUtils; +import ch.njol.skript.util.PotionEffectUtils; +import ch.njol.skript.util.StringMode; +import ch.njol.util.StringUtils; +import ch.njol.yggdrasil.Fields; +import io.papermc.paper.world.MoonPhase; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Difficulty; @@ -80,37 +94,19 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; - -import ch.njol.skript.Skript; -import ch.njol.skript.SkriptConfig; -import ch.njol.skript.aliases.Aliases; -import ch.njol.skript.aliases.ItemType; -import ch.njol.skript.bukkitutil.EnchantmentUtils; -import ch.njol.skript.bukkitutil.ItemUtils; -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.ConfigurationSerializer; -import ch.njol.skript.classes.EnumClassInfo; -import ch.njol.skript.classes.Parser; -import ch.njol.skript.classes.Serializer; -import ch.njol.skript.classes.registry.RegistryClassInfo; -import ch.njol.skript.entity.EntityData; -import ch.njol.skript.expressions.ExprDamageCause; -import ch.njol.skript.expressions.base.EventValueExpression; -import ch.njol.skript.lang.ParseContext; -import ch.njol.skript.lang.util.SimpleLiteral; -import ch.njol.skript.localization.Language; -import ch.njol.skript.registrations.Classes; -import ch.njol.skript.util.BlockUtils; -import ch.njol.skript.util.PotionEffectUtils; -import ch.njol.skript.util.StringMode; -import ch.njol.util.StringUtils; -import ch.njol.yggdrasil.Fields; -import io.papermc.paper.world.MoonPhase; import org.jetbrains.annotations.Nullable; -/** - * @author Peter Güttinger - */ +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + public class BukkitClasses { public BukkitClasses() {} @@ -1527,6 +1523,7 @@ public String toVariableNameString(EnchantmentOffer eo) { .name("Transform Reason") .description("Represents a transform reason of an entity transform event.") .since("2.8.0")); + Classes.registerClass(new EnumClassInfo<>(EntityPotionEffectEvent.Cause.class, "entitypotioncause", "entity potion causes") .user("(entity )?potion ?effect ?cause") .name("Entity Potion Cause") diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java index e5e42bf1423..c88a98bdf88 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java @@ -29,6 +29,7 @@ import ch.njol.skript.entity.EntityData; import ch.njol.skript.entity.RabbitData; import ch.njol.skript.util.BlockUtils; +import ch.njol.skript.util.Color; import ch.njol.skript.util.Date; import ch.njol.skript.util.EnchantmentType; import ch.njol.skript.util.Experience; @@ -75,20 +76,28 @@ public DefaultComparators() {} static { // Number - Number - Comparators.registerComparator(Number.class, Number.class, new Comparator() { + Comparators.registerComparator(Number.class, Number.class, new Comparator<>() { @Override public Relation compare(Number n1, Number n2) { if (n1 instanceof Long && n2 instanceof Long) return Relation.get(n1.longValue() - n2.longValue()); - Double d1 = n1.doubleValue(), - d2 = n2.doubleValue(); + double epsilon = Skript.EPSILON; + @SuppressWarnings("WrapperTypeMayBePrimitive") Double d1, d2; + if (n1 instanceof Float || n2 instanceof Float) { + d1 = (double) n1.floatValue(); + d2 = (double) n2.floatValue(); + epsilon = Math.min(d1, d2) * 1e-6; // dynamic epsilon + } else { + d1 = n1.doubleValue(); + d2 = n2.doubleValue(); + } if (d1.isNaN() || d2.isNaN()) { return Relation.SMALLER; } else if (d1.isInfinite() || d2.isInfinite()) { return d1 > d2 ? Relation.GREATER : d1 < d2 ? Relation.SMALLER : Relation.EQUAL; } else { double diff = d1 - d2; - if (Math.abs(diff) < Skript.EPSILON) + if (Math.abs(diff) < epsilon) return Relation.EQUAL; return Relation.get(diff); } @@ -656,6 +665,11 @@ public boolean supportsOrdering() { // Potion Effect Type Comparators.registerComparator(PotionEffectType.class, PotionEffectType.class, (one, two) -> Relation.get(one.equals(two))); + + // Color - Color + Comparators.registerComparator(Color.class, Color.class, (one, two) -> Relation.get(one.asBukkitColor().equals(two.asBukkitColor()))); + Comparators.registerComparator(Color.class, org.bukkit.Color.class, (one, two) -> Relation.get(one.asBukkitColor().equals(two))); + Comparators.registerComparator(org.bukkit.Color.class, org.bukkit.Color.class, (one, two) -> Relation.get(one.equals(two))); } } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index 90c2c0c43bd..62997b3a76b 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -31,6 +31,7 @@ import ch.njol.skript.registrations.DefaultClasses; import ch.njol.skript.util.Color; import ch.njol.skript.util.ColorRGB; +import ch.njol.skript.util.Contract; import ch.njol.skript.util.Date; import ch.njol.util.Math2; import ch.njol.util.StringUtils; @@ -42,7 +43,9 @@ import org.bukkit.entity.Player; import org.bukkit.util.Vector; import org.jetbrains.annotations.Nullable; -import ch.njol.skript.util.Contract; +import org.joml.AxisAngle4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; import java.math.BigDecimal; import java.math.RoundingMode; @@ -525,19 +528,25 @@ public Long[] executeSimple(Object[][] params) { Functions.registerFunction(new SimpleJavaFunction("rgb", new Parameter[] { new Parameter<>("red", DefaultClasses.LONG, true, null), new Parameter<>("green", DefaultClasses.LONG, true, null), - new Parameter<>("blue", DefaultClasses.LONG, true, null) + new Parameter<>("blue", DefaultClasses.LONG, true, null), + new Parameter<>("alpha", DefaultClasses.LONG, true, new SimpleLiteral<>(255L,true)) }, DefaultClasses.COLOR, true) { @Override public ColorRGB[] executeSimple(Object[][] params) { Long red = (Long) params[0][0]; Long green = (Long) params[1][0]; Long blue = (Long) params[2][0]; + Long alpha = (Long) params[3][0]; - return CollectionUtils.array(new ColorRGB(red.intValue(), green.intValue(), blue.intValue())); + return CollectionUtils.array(ColorRGB.fromRGBA(red.intValue(), green.intValue(), blue.intValue(), alpha.intValue())); } - }).description("Returns a RGB color from the given red, green and blue parameters.") - .examples("dye player's leggings rgb(120, 30, 45)") - .since("2.5"); + }).description("Returns a RGB color from the given red, green and blue parameters. Alpha values can be added optionally, " + + "but these only take affect in certain situations, like text display backgrounds.") + .examples( + "dye player's leggings rgb(120, 30, 45)", + "set the colour of a text display to rgb(10, 50, 100, 50)" + ) + .since("2.5, INSERT VERSION (alpha)"); Functions.registerFunction(new SimpleJavaFunction("player", new Parameter[] { new Parameter<>("nameOrUUID", DefaultClasses.STRING, true, null), @@ -634,6 +643,50 @@ public String[] executeSimple(Object[][] params) { "concat(\"foo \", 100, \" bar\") # foo 100 bar" ).since("2.9.0"); + // joml functions - for display entities + { + if (Skript.classExists("org.joml.Quaternionf")) { + Functions.registerFunction(new SimpleJavaFunction<>("quaternion", new Parameter[]{ + new Parameter<>("w", DefaultClasses.NUMBER, true, null), + new Parameter<>("x", DefaultClasses.NUMBER, true, null), + new Parameter<>("y", DefaultClasses.NUMBER, true, null), + new Parameter<>("z", DefaultClasses.NUMBER, true, null) + }, Classes.getExactClassInfo(Quaternionf.class), true) { + @Override + public Quaternionf[] executeSimple(Object[][] params) { + double w = ((Number) params[0][0]).doubleValue(); + double x = ((Number) params[1][0]).doubleValue(); + double y = ((Number) params[2][0]).doubleValue(); + double z = ((Number) params[3][0]).doubleValue(); + return CollectionUtils.array(new Quaternionf(x, y, z, w)); + } + }) + .description("Returns a quaternion from the given W, X, Y and Z parameters. ") + .examples("quaternion(1, 5.6, 45.21, 10)") + .since("INSERT VERSION"); + } + + if (Skript.classExists("org.joml.AxisAngle4f")) { + Functions.registerFunction(new SimpleJavaFunction<>("axisAngle", new Parameter[]{ + new Parameter<>("angle", DefaultClasses.NUMBER, true, null), + new Parameter<>("axis", DefaultClasses.VECTOR, true, null) + }, Classes.getExactClassInfo(Quaternionf.class), true) { + @Override + public Quaternionf[] executeSimple(Object[][] params) { + float angle = (float) (((Number) params[0][0]).floatValue() / 180 * Math.PI); + Vector v = ((Vector) params[1][0]); + if (v.isZero() || !Double.isFinite(v.getX()) || !Double.isFinite(v.getY()) || !Double.isFinite(v.getZ())) + return new Quaternionf[0]; + Vector3f axis = ((Vector) params[1][0]).toVector3f(); + return CollectionUtils.array(new Quaternionf(new AxisAngle4f(angle, axis))); + } + }) + .description("Returns a quaternion from the given angle (in degrees) and axis (as a vector). This represents a rotation around the given axis by the given angle.") + .examples("axisangle(90, (vector from player's facing))") + .since("INSERT VERSION"); + } + } // end joml functions + } - + } diff --git a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java index 660c1cc04ba..5485a852386 100644 --- a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java @@ -18,10 +18,12 @@ */ package ch.njol.skript.classes.data; +import ch.njol.skript.Skript; import ch.njol.skript.SkriptConfig; import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.classes.Parser; import ch.njol.skript.classes.Serializer; +import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.VariableString; import ch.njol.skript.lang.util.SimpleLiteral; @@ -32,6 +34,7 @@ import ch.njol.util.StringUtils; import ch.njol.yggdrasil.Fields; import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; import java.util.regex.Pattern; @@ -627,5 +630,40 @@ public boolean mustSyncDeserialization() { return false; } })); + + // joml type - for display entities + if (Skript.classExists("org.joml.Quaternionf")) + Classes.registerClass(new ClassInfo<>(Quaternionf.class, "quaternion") + .user("quaternionf?s?") + .name("Quaternion") + .description("Quaternions are four dimensional vectors, often used for representing rotations.") + .since("INSERT VERSION") + .parser(new Parser<>() { + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(Quaternionf quaternion, int flags) { + return "w:" + Skript.toString(quaternion.w()) + ", x:" + Skript.toString(quaternion.x()) + ", y:" + Skript.toString(quaternion.y()) + ", z:" + Skript.toString(quaternion.z()); + } + + @Override + public String toVariableNameString(Quaternionf quaternion) { + return quaternion.w() + "," + quaternion.x() + "," + quaternion.y() + "," + quaternion.z(); + } + }) + .defaultExpression(new EventValueExpression<>(Quaternionf.class)) + .cloner(quaternion -> { + try { + // Implements cloneable, but doesn't return a Quaternionf. + // org.joml improper override. Returns Object. + return (Quaternionf) quaternion.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + })); + } + } diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index b0538c25f91..86c29a9d4fe 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -1,33 +1,5 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ package ch.njol.skript.classes.data; -import java.io.StreamCorruptedException; -import java.util.Iterator; -import java.util.Locale; -import java.util.regex.Pattern; - -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemData; @@ -64,12 +36,17 @@ import ch.njol.skript.util.visual.VisualEffect; import ch.njol.skript.util.visual.VisualEffects; import ch.njol.yggdrasil.Fields; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; +import java.io.StreamCorruptedException; import java.util.Arrays; +import java.util.Iterator; +import java.util.Locale; +import java.util.regex.Pattern; -/** - * @author Peter Güttinger - */ @SuppressWarnings("rawtypes") public class SkriptClasses { public SkriptClasses() {} diff --git a/src/main/java/ch/njol/skript/effects/EffColorItems.java b/src/main/java/ch/njol/skript/effects/EffColorItems.java index aece411cb59..9f8a7ad5657 100644 --- a/src/main/java/ch/njol/skript/effects/EffColorItems.java +++ b/src/main/java/ch/njol/skript/effects/EffColorItems.java @@ -94,7 +94,7 @@ protected Color[] get(Event e) { if (r == null || g == null || b == null) return null; - return CollectionUtils.array(new ColorRGB(r.intValue(), g.intValue(), b.intValue())); + return CollectionUtils.array(ColorRGB.fromRGB(r.intValue(), g.intValue(), b.intValue())); } @Override diff --git a/src/main/java/ch/njol/skript/effects/EffVectorRotateAroundAnother.java b/src/main/java/ch/njol/skript/effects/EffVectorRotateAroundAnother.java deleted file mode 100644 index 2ffddda71d9..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffVectorRotateAroundAnother.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.skript.effects; - -import org.bukkit.event.Event; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.Skript; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Examples; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; -import ch.njol.util.VectorMath; - -@Name("Vectors - Rotate Around Vector") -@Description("Rotates one or more vectors around another vector") -@Examples("rotate {_v} around vector 1, 0, 0 by 90") -@Since("2.2-dev28") -public class EffVectorRotateAroundAnother extends Effect { - - static { - Skript.registerEffect(EffVectorRotateAroundAnother.class, "rotate %vectors% around %vector% by %number% [degrees]"); - } - - @SuppressWarnings("null") - private Expression vectors, axis; - - @SuppressWarnings("null") - private Expression degree; - - @SuppressWarnings({"unchecked", "null"}) - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean kleenean, ParseResult parseResult) { - vectors = (Expression) exprs[0]; - axis = (Expression) exprs[1]; - degree = (Expression) exprs[2]; - return true; - } - - @SuppressWarnings("null") - @Override - protected void execute(Event event) { - Vector axis = this.axis.getSingle(event); - Number angle = degree.getSingle(event); - if (axis == null || angle == null) - return; - for (Vector vector : vectors.getArray(event)) - VectorMath.rot(vector, axis, angle.doubleValue()); - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return "rotate " + vectors.toString(event, debug) + " around " + axis.toString(event, debug) + " by " + degree.toString(event, debug) + "degrees"; - } - -} diff --git a/src/main/java/ch/njol/skript/effects/EffVectorRotateXYZ.java b/src/main/java/ch/njol/skript/effects/EffVectorRotateXYZ.java deleted file mode 100644 index a24ad0dcd51..00000000000 --- a/src/main/java/ch/njol/skript/effects/EffVectorRotateXYZ.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.skript.effects; - -import org.bukkit.event.Event; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.Skript; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Examples; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; -import ch.njol.util.VectorMath; - -@Name("Vectors - Rotate around XYZ") -@Description("Rotates one or more vectors around the x, y, or z axis by some amount of degrees") -@Examples({ - "rotate {_v} around x-axis by 90", - "rotate {_v} around y-axis by 90", - "rotate {_v} around z-axis by 90 degrees" -}) -@Since("2.2-dev28") -public class EffVectorRotateXYZ extends Effect { - - static { - Skript.registerEffect(EffVectorRotateXYZ.class, "rotate %vectors% around (0¦x|1¦y|2¦z)(-| )axis by %number% [degrees]"); - } - - private final static Character[] axes = new Character[] {'x', 'y', 'z'}; - - @SuppressWarnings("null") - private Expression vectors; - - @SuppressWarnings("null") - private Expression degree; - private int axis; - - @Override - @SuppressWarnings({"unchecked", "null"}) - public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - vectors = (Expression) expressions[0]; - degree = (Expression) expressions[1]; - axis = parseResult.mark; - return true; - } - - @Override - @SuppressWarnings("null") - protected void execute(Event event) { - Number angle = degree.getSingle(event); - if (angle == null) - return; - switch (axis) { - case 0: - for (Vector vector : vectors.getArray(event)) - VectorMath.rotX(vector, angle.doubleValue()); - break; - case 1: - for (Vector vector : vectors.getArray(event)) - VectorMath.rotY(vector, angle.doubleValue()); - break; - case 2: - for (Vector vector : vectors.getArray(event)) - VectorMath.rotZ(vector, angle.doubleValue()); - } - } - - @Override - public String toString(@Nullable Event event, boolean debug) { - return "rotate " + vectors.toString(event, debug) + " around " + axes[axis] + "-axis" + " by " + degree.toString(event, debug) + "degrees"; - } - -} diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index 24b8620a608..9f72ffec5b8 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -311,16 +311,12 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("warden", Warden.class); } - if (Skript.isRunningMinecraft(1,19,3)) + if (Skript.isRunningMinecraft(1, 19, 3)) addSimpleEntity("camel", Camel.class); - if (Skript.isRunningMinecraft(1,19,4)) { + if (Skript.isRunningMinecraft(1, 19, 4)) { addSimpleEntity("sniffer", Sniffer.class); - addSimpleEntity("text display", TextDisplay.class); - addSimpleEntity("item display", ItemDisplay.class); - addSimpleEntity("block display", BlockDisplay.class); addSimpleEntity("interaction", Interaction.class); - addSuperEntity("display", Display.class); } if (Skript.isRunningMinecraft(1, 20, 3)) { @@ -337,7 +333,7 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("zombie", Zombie.class); // Register squid after glow squid to make sure both work addSimpleEntity("squid", Squid.class); - + // SuperTypes addSuperEntity("human", HumanEntity.class); addSuperEntity("damageable", Damageable.class); @@ -358,12 +354,14 @@ private static void addSuperEntity(String codeName, Class enti addSuperEntity("any fireball", Fireball.class); addSuperEntity("illager", Illager.class); addSuperEntity("spellcaster", Spellcaster.class); - if (Skript.classExists("org.bukkit.entity.Raider")) // Introduced in Spigot 1.14 + if (Skript.classExists("org.bukkit.entity.Raider")) // 1.14 addSuperEntity("raider", Raider.class); - if (Skript.classExists("org.bukkit.entity.Enemy")) // Introduced in Spigot 1.19.3 + if (Skript.classExists("org.bukkit.entity.Enemy")) // 1.19.3 addSuperEntity("enemy", Enemy.class); + if (Skript.classExists("org.bukkit.entity.Display")) // 1.19.4 + addSuperEntity("display", Display.class); } - + static { final String[] codeNames = new String[types.size()]; int i = 0; diff --git a/src/main/java/ch/njol/skript/expressions/ExprBlockData.java b/src/main/java/ch/njol/skript/expressions/ExprBlockData.java index 1a9401d1439..210d653842e 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBlockData.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBlockData.java @@ -1,85 +1,76 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ package ch.njol.skript.expressions; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; import ch.njol.util.coll.CollectionUtils; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; @Name("Block Data") -@Description("Get the block data associated with a block. This data can also be used to set blocks.") -@Examples({"set {data} to block data of target block", - "set block at player to {data}", - "set block data of target block to oak_stairs[facing=south;waterlogged=true]"}) -@RequiredPlugins("Minecraft 1.13+") -@Since("2.5, 2.5.2 (set)") -public class ExprBlockData extends SimplePropertyExpression { - +@Description({ + "Get the block data associated with a block.", + "This data can also be used to set blocks." +}) +@Examples({ + "set {_data} to block data of target block", + "set block at player to {_data}", + "", + "set block data of target block to oak_stairs[facing=south;waterlogged=true]" +}) +@Since("2.5, 2.5.2 (set), INSERT VERSION (block displays)") +public class ExprBlockData extends SimplePropertyExpression { + static { - if (Skript.classExists("org.bukkit.block.data.BlockData")) - register(ExprBlockData.class, BlockData.class, "block[ ]data", "blocks"); + String types = Skript.isRunningMinecraft(1, 19, 4) ? "blocks/displays" : "blocks"; + register(ExprBlockData.class, BlockData.class, "block[ ]data", types); } - - @Nullable + @Override - public BlockData convert(Block block) { - return block.getBlockData(); + public @Nullable BlockData convert(Object object) { + if (object instanceof Block block) + return block.getBlockData(); + if (object instanceof BlockDisplay blockDisplay) + return blockDisplay.getBlock(); + return null; + } - - @Nullable + @Override - public Class[] acceptChange(ChangeMode mode) { + public Class @Nullable [] acceptChange(ChangeMode mode) { if (mode == ChangeMode.SET) return CollectionUtils.array(BlockData.class); return null; } - + @Override - public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { - if (delta == null) - return; - + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + assert delta != null; // reset/delete not supported BlockData blockData = ((BlockData) delta[0]); - for (Block block : getExpr().getArray(e)) { - block.setBlockData(blockData); + for (Object object : getExpr().getArray(event)) { + if (object instanceof Block block) { + block.setBlockData(blockData); + } else if (object instanceof BlockDisplay blockDisplay) { + blockDisplay.setBlock(blockData); + } } } - - @Override - protected String getPropertyName() { - return "block data"; - } - + @Override public Class getReturnType() { return BlockData.class; } - + + @Override + protected String getPropertyName() { + return "block data"; + } + } diff --git a/src/main/java/ch/njol/skript/expressions/ExprColorOf.java b/src/main/java/ch/njol/skript/expressions/ExprColorOf.java index 518402fc192..e46cfa1fc33 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprColorOf.java +++ b/src/main/java/ch/njol/skript/expressions/ExprColorOf.java @@ -18,74 +18,90 @@ */ package ch.njol.skript.expressions; -import java.util.ArrayList; -import java.util.List; - import ch.njol.skript.Skript; -import org.bukkit.DyeColor; -import org.bukkit.FireworkEffect; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Item; -import org.bukkit.event.Event; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.Colorable; -import org.bukkit.material.MaterialData; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.aliases.ItemType; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import org.skriptlang.skript.bukkit.displays.DisplayData; import ch.njol.skript.expressions.base.PropertyExpression; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.util.Color; +import ch.njol.skript.util.ColorRGB; import ch.njol.skript.util.SkriptColor; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import org.bukkit.DyeColor; +import org.bukkit.FireworkEffect; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Display; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.Colorable; +import org.bukkit.material.MaterialData; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; @Name("Color of") -@Description("The color of an item, can also be used to color chat messages with \"<%color of ...%>this text is colored!\".") -@Examples({"on click on wool:", - " message \"This wool block is <%color of block%>%color of block%!\"", - " set the color of the block to black"}) -@Since("1.2") +@Description({ + "The color of an item, entity, block, firework effect, or text display.", + "This can also be used to color chat messages with \"<%color of ...%>this text is colored!\".", + "Do note that firework effects support setting, adding, removing, resetting, and deleting; text displays support " + + "setting and resetting; and items, entities, and blocks only support setting, and only for very few items/blocks." +}) +@Examples({ + "on click on wool:", + "\tmessage \"This wool block is <%color of block%>%color of block%!\"", + "\tset the color of the block to black" +}) +@Since("1.2, INSERT VERSION (displays)") public class ExprColorOf extends PropertyExpression { static { - register(ExprColorOf.class, Color.class, "colo[u]r[s]", "blocks/itemtypes/entities/fireworkeffects"); + String types = "blocks/itemtypes/entities/fireworkeffects"; + if (Skript.isRunningMinecraft(1, 19, 4)) + types += "/displays"; + register(ExprColorOf.class, Color.class, "colo[u]r[s]", types); } - - @SuppressWarnings("null") + @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { setExpr(exprs[0]); return true; } - - @SuppressWarnings("null") + @Override - protected Color[] get(Event e, Object[] source) { + protected Color[] get(Event event, Object[] source) { if (source instanceof FireworkEffect[]) { List colors = new ArrayList<>(); - for (FireworkEffect effect : (FireworkEffect[]) source) { effect.getColors().stream() .map(SkriptColor::fromBukkitColor) .forEach(colors::add); } - - if (colors.size() == 0) - return null; return colors.toArray(new Color[0]); } - return get(source, o -> { - Colorable colorable = getColorable(o); - + return get(source, object -> { + if (object instanceof Display) { + if (!(object instanceof TextDisplay display)) + return null; + if (display.isDefaultBackground()) + return ColorRGB.fromBukkitColor(DisplayData.DEFAULT_BACKGROUND_COLOR); + org.bukkit.Color bukkitColor = display.getBackgroundColor(); + if (bukkitColor == null) + return null; + return ColorRGB.fromBukkitColor(bukkitColor); + } + Colorable colorable = getColorable(object); if (colorable == null) return null; DyeColor dyeColor = colorable.getColor(); @@ -96,65 +112,63 @@ protected Color[] get(Event e, Object[] source) { } @Override - public Class getReturnType() { - return Color.class; - } - - @Override - public String toString(@Nullable Event e, boolean debug) { - return "color of " + getExpr().toString(e, debug); - } - - @Override - @Nullable - public Class[] acceptChange(ChangeMode mode) { + public Class @Nullable [] acceptChange(ChangeMode mode) { Class returnType = getExpr().getReturnType(); - if (FireworkEffect.class.isAssignableFrom(returnType)) + if (returnType.isAssignableFrom(FireworkEffect.class)) return CollectionUtils.array(Color[].class); - if (mode != ChangeMode.SET && !getExpr().isSingle()) - return null; - - if (Entity.class.isAssignableFrom(returnType)) + // double assignable checks are to allow both parent and child types, since variables return Object + // This does mean we have to be more stringent in checking the validity of the change mode in change() itself. + if ((returnType.isAssignableFrom(Display.class) || Display.class.isAssignableFrom(returnType)) && (mode == ChangeMode.RESET || mode == ChangeMode.SET)) return CollectionUtils.array(Color.class); - else if (Block.class.isAssignableFrom(returnType)) - return CollectionUtils.array(Color.class); - if (ItemType.class.isAssignableFrom(returnType)) + + // the following only support SET + if (mode != ChangeMode.SET) + return null; + if (returnType.isAssignableFrom(Entity.class) + || Entity.class.isAssignableFrom(returnType) + || returnType.isAssignableFrom(Block.class) + || Block.class.isAssignableFrom(returnType) + || returnType.isAssignableFrom(ItemType.class) + ) { return CollectionUtils.array(Color.class); + } return null; } - @SuppressWarnings("deprecated") @Override - public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { - if (delta == null) - return; - DyeColor color = ((Color) delta[0]).asDyeColor(); - - for (Object o : getExpr().getArray(e)) { - if (o instanceof Item || o instanceof ItemType) { - ItemStack stack = o instanceof Item ? ((Item) o).getItemStack() : ((ItemType) o).getRandom(); - + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Color color = null; + if (delta != null) + color = (Color) delta[0]; + for (Object object : getExpr().getArray(event)) { + if (object instanceof Item || object instanceof ItemType) { + if (mode != ChangeMode.SET) + return; + assert color != null; + ItemStack stack = object instanceof Item ? ((Item) object).getItemStack() : ((ItemType) object).getRandom(); if (stack == null) continue; MaterialData data = stack.getData(); - if (!(data instanceof Colorable)) continue; - ((Colorable) data).setColor(color); + ((Colorable) data).setColor(color.asDyeColor()); stack.setData(data); - if (o instanceof Item) - ((Item) o).setItemStack(stack); - } else if (o instanceof Block || o instanceof Colorable) { - Colorable colorable = getColorable(o); + if (object instanceof Item item) + item.setItemStack(stack); + } else if (object instanceof Block || object instanceof Colorable) { + if (mode != ChangeMode.SET) + return; + Colorable colorable = getColorable(object); + assert color != null; if (colorable != null) { try { - colorable.setColor(color); + colorable.setColor(color.asDyeColor()); } catch (UnsupportedOperationException ex) { // https://github.com/SkriptLang/Skript/issues/2931 Skript.error("Tried setting the color of a bed, but this isn't possible in your Minecraft version, " + @@ -162,9 +176,18 @@ public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { "Instead, set the block to right material, such as a blue bed."); // Let's just assume it's a bed } } - } else if (o instanceof FireworkEffect) { + } else if (object instanceof TextDisplay display) { + switch (mode) { + case RESET -> display.setDefaultBackground(true); + case SET -> { + assert color != null; + if (display.isDefaultBackground()) + display.setDefaultBackground(false); + display.setBackgroundColor(color.asBukkitColor()); + } + } + } else if (object instanceof FireworkEffect effect) { Color[] input = (Color[]) delta; - FireworkEffect effect = ((FireworkEffect) o); switch (mode) { case ADD: for (Color c : input) @@ -191,7 +214,16 @@ public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { } } - @SuppressWarnings("deprecated") + @Override + public Class getReturnType() { + return Color.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "color of " + getExpr().toString(event, debug); + } + @Nullable private Colorable getColorable(Object colorable) { if (colorable instanceof Item || colorable instanceof ItemType) { @@ -201,12 +233,10 @@ private Colorable getColorable(Object colorable) { if (item == null) return null; MaterialData data = item.getData(); - if (data instanceof Colorable) return (Colorable) data; } else if (colorable instanceof Block) { BlockState state = ((Block) colorable).getState(); - if (state instanceof Colorable) return (Colorable) state; } else if (colorable instanceof Colorable) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprItemFrameSlot.java b/src/main/java/ch/njol/skript/expressions/ExprItemFrameSlot.java deleted file mode 100644 index 6a67e34aa4d..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprItemFrameSlot.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.skript.expressions; - -import org.bukkit.entity.Entity; -import org.bukkit.entity.Item; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.ThrowableProjectile; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.Skript; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Examples; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.RequiredPlugins; -import ch.njol.skript.doc.Since; -import ch.njol.skript.expressions.base.SimplePropertyExpression; -import ch.njol.skript.util.slot.DroppedItemSlot; -import ch.njol.skript.util.slot.ItemFrameSlot; -import ch.njol.skript.util.slot.Slot; -import ch.njol.skript.util.slot.ThrowableProjectileSlot; - -@Name("Item of an Entity") -@Description("An item associated with an entity. For dropped item entities, it gets, obviously, the item that was dropped. " - + "For item frames, the item inside the frame is returned. For throwable projectiles (snowballs, enderpearls etc.)," - + "it gets the displayed item. Other entities do not have items associated with them.") -@Examples("") -@Since("2.2-dev35, 2.2-dev36 (improved), 2.5.2 (throwable projectiles)") -@RequiredPlugins("Minecraft 1.15.2+ (throwable projectiles)") -public class ExprItemFrameSlot extends SimplePropertyExpression { - - private static final boolean PROJECTILE_SUPPORT = Skript.classExists("org.bukkit.entity.ThrowableProjectile"); - - static { - register(ExprItemFrameSlot.class, Slot.class, "item", "entities"); - } - - @Override - @Nullable - public Slot convert(Entity e) { - if (e instanceof ItemFrame) - return new ItemFrameSlot((ItemFrame) e); - else if (e instanceof Item) - return new DroppedItemSlot((Item) e); - else if (PROJECTILE_SUPPORT && e instanceof ThrowableProjectile) - return new ThrowableProjectileSlot((ThrowableProjectile) e); - return null; // Other entities don't have associated items - } - - @Override - protected String getPropertyName() { - return "item of entity"; - } - - @Override - public Class getReturnType() { - return Slot.class; - } -} diff --git a/src/main/java/ch/njol/skript/expressions/ExprVectorXYZ.java b/src/main/java/ch/njol/skript/expressions/ExprVectorXYZ.java deleted file mode 100644 index 82a0c03d790..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprVectorXYZ.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ -package ch.njol.skript.expressions; - -import org.bukkit.event.Event; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.classes.Changer; -import ch.njol.skript.classes.Changer.ChangeMode; -import ch.njol.skript.doc.Description; -import ch.njol.skript.doc.Examples; -import ch.njol.skript.doc.Name; -import ch.njol.skript.doc.Since; -import ch.njol.skript.expressions.base.SimplePropertyExpression; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.util.Kleenean; -import ch.njol.util.coll.CollectionUtils; - -@Name("Vectors - XYZ Component") -@Description("Gets or changes the x, y or z component of a vector.") -@Examples({ - "set {_v} to vector 1, 2, 3", - "send \"%x of {_v}%, %y of {_v}%, %z of {_v}%\"", - "add 1 to x of {_v}", - "add 2 to y of {_v}", - "add 3 to z of {_v}", - "send \"%x of {_v}%, %y of {_v}%, %z of {_v}%\"", - "set x component of {_v::*} to 1", - "set y component of {_v::*} to 2", - "set z component of {_v::*} to 3", - "send \"%x component of {_v::*}%, %y component of {_v::*}%, %z component of {_v::*}%\"" -}) -@Since("2.2-dev28") -public class ExprVectorXYZ extends SimplePropertyExpression { - - static { - // TODO: Combine with ExprCoordinates for 2.9 - register(ExprVectorXYZ.class, Number.class, "[vector] (0:x|1:y|2:z) [component[s]]", "vectors"); - } - - private final static Character[] axes = new Character[] {'x', 'y', 'z'}; - - private int axis; - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - super.init(exprs, matchedPattern, isDelayed, parseResult); - axis = parseResult.mark; - return true; - } - - @Override - public Number convert(Vector vector) { - return axis == 0 ? vector.getX() : (axis == 1 ? vector.getY() : vector.getZ()); - } - - @Override - @SuppressWarnings("null") - public Class[] acceptChange(ChangeMode mode) { - if ((mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.SET) - && Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Vector.class)) - return CollectionUtils.array(Number.class); - return null; - } - - @Override - public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { - assert delta != null; - Vector[] vectors = getExpr().getArray(event); - if (vectors.length == 0) - return; - double deltaValue = ((Number) delta[0]).doubleValue(); - switch (mode) { - case REMOVE: - deltaValue = -deltaValue; - //$FALL-THROUGH$ - case ADD: - for (Vector vector : vectors) { - if (axis == 0) - vector.setX(vector.getX() + deltaValue); - else if (axis == 1) - vector.setY(vector.getY() + deltaValue); - else - vector.setZ(vector.getZ() + deltaValue); - } - break; - case SET: - for (Vector vector : vectors) { - if (axis == 0) - vector.setX(deltaValue); - else if (axis == 1) - vector.setY(deltaValue); - else - vector.setZ(deltaValue); - } - break; - default: - assert false; - return; - } - getExpr().change(event, vectors, ChangeMode.SET); - } - - @Override - public Class getReturnType() { - return Number.class; - } - - @Override - protected String getPropertyName() { - return axes[axis] + " component"; - } -} diff --git a/src/main/java/ch/njol/skript/expressions/ExprXYZComponent.java b/src/main/java/ch/njol/skript/expressions/ExprXYZComponent.java new file mode 100644 index 00000000000..a6db3c7e92e --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprXYZComponent.java @@ -0,0 +1,215 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.joml.Quaternionf; + +import java.util.Locale; + +@Name("Vector/Quaternion - WXYZ Component") +@Description({ + "Gets or changes the W, X, Y or Z component of vectors/quaternions.", + "You cannot use the W component with vectors; it is for quaternions only." +}) +@Examples({ + "set {_v} to vector 1, 2, 3", + "send \"%x of {_v}%, %y of {_v}%, %z of {_v}%\"", + "add 1 to x of {_v}", + "add 2 to y of {_v}", + "add 3 to z of {_v}", + "send \"%x of {_v}%, %y of {_v}%, %z of {_v}%\"", + "set x component of {_v} to 1", + "set y component of {_v} to 2", + "set z component of {_v} to 3", + "send \"%x component of {_v}%, %y component of {_v}%, %z component of {_v}%\"" +}) +@Since("2.2-dev28, INSERT VERSION (quaternions)") +public class ExprXYZComponent extends SimplePropertyExpression { + + private static final boolean IS_RUNNING_1194 = Skript.isRunningMinecraft(1, 19, 4); + + static { + String types = "vectors"; + if (IS_RUNNING_1194) + types += "/quaternions"; + register(ExprXYZComponent.class, Number.class, "[vector|quaternion] (:w|:x|:y|:z) [component[s]]", types); + } + + private enum Axis { + W, + X, + Y, + Z; + } + + private ExprXYZComponent.@UnknownNullability Axis axis; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + axis = Axis.valueOf(parseResult.tags.get(0).toUpperCase(Locale.ENGLISH)); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + public @Nullable Number convert(Object object) { + if (object instanceof Vector vector) { + return switch (axis) { + case W -> null; + case X -> vector.getX(); + case Y -> vector.getY(); + case Z -> vector.getZ(); + }; + } else if (object instanceof Quaternionf quaternion) { + return switch (axis) { + case W -> quaternion.w(); + case X -> quaternion.x(); + case Y -> quaternion.y(); + case Z -> quaternion.z(); + }; + } + return null; + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + if ((mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.SET)) { + boolean acceptsChange; + if (IS_RUNNING_1194) { + acceptsChange = Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Vector.class, Quaternionf.class); + } else { + acceptsChange = Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Vector.class); + } + if (acceptsChange) + return CollectionUtils.array(Number.class); + } + return null; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + assert delta != null; // reset/delete not supported + Object[] objects = getExpr().getArray(event); + double value = ((Number) delta[0]).doubleValue(); + + boolean hasVectors = false; + boolean hasQuaternions = false; + boolean hasInvalidInput = false; + + for (Object object : objects) { + if (object instanceof Vector vector) { + changeVector(vector, axis, value, mode); + hasVectors = true; + } else if (object instanceof Quaternionf quaternion) { + changeQuaternion(quaternion, axis, (float) value, mode); + hasQuaternions = true; + } else { + hasInvalidInput = true; + } + } + + // don't SET the expression if there were invalid inputs + if (hasInvalidInput) + return; + + // covers the edge case where an expression can be set to Vector but returns Quaternions, or similar. + if (hasVectors && !Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Vector.class)) + return; + if (hasQuaternions && !Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Quaternionf.class)) + return; + getExpr().change(event, objects, ChangeMode.SET); + } + + /** + * Helper method to modify a single vector's component. Does not call .change(). + * + * @param vector the vector to modify + * @param value the value to modify by + * @param mode the change mode to determine the modification type + */ + private static void changeVector(Vector vector, Axis axis, double value, ChangeMode mode) { + if (axis == Axis.W) + return; + switch (mode) { + case REMOVE: + value = -value; + //$FALL-THROUGH$ + case ADD: + switch (axis) { + case X -> vector.setX(vector.getX() + value); + case Y -> vector.setY(vector.getY() + value); + case Z -> vector.setZ(vector.getZ() + value); + } + break; + case SET: + switch (axis) { + case X -> vector.setX(value); + case Y -> vector.setY(value); + case Z -> vector.setZ(value); + } + break; + default: + assert false; + } + } + + /** + * Helper method to modify a single quaternion's component. Does not call .change(). + * + * @param quaternion the vector to modify + * @param value the value to modify by + * @param mode the change mode to determine the modification type + */ + private static void changeQuaternion(Quaternionf quaternion, Axis axis, float value, ChangeMode mode) { + float x = quaternion.x(); + float y = quaternion.y(); + float z = quaternion.z(); + float w = quaternion.w(); + switch (mode) { + case REMOVE: + value = -value; + //$FALL-THROUGH$ + case ADD: + switch (axis) { + case W -> w += value; + case X -> x += value; + case Y -> y += value; + case Z -> z += value; + } + break; + case SET: + switch (axis) { + case W -> w = value; + case X -> x = value; + case Y -> y = value; + case Z -> z = value; + } + break; + } + quaternion.set(x, y, z, w); + } + + @Override + public Class getReturnType() { + return Number.class; + } + + @Override + protected String getPropertyName() { + return axis.name().toLowerCase(Locale.ENGLISH) + " component"; + } + +} diff --git a/src/main/java/ch/njol/skript/registrations/DefaultClasses.java b/src/main/java/ch/njol/skript/registrations/DefaultClasses.java index 2065e6a5ff0..e5cf895ddd1 100644 --- a/src/main/java/ch/njol/skript/registrations/DefaultClasses.java +++ b/src/main/java/ch/njol/skript/registrations/DefaultClasses.java @@ -32,31 +32,34 @@ // When using these fields, be aware all ClassInfo's must be registered! public class DefaultClasses { - + public static ClassInfo OBJECT = getClassInfo(Object.class); - + + // Java public static ClassInfo NUMBER = getClassInfo(Number.class); public static ClassInfo LONG = getClassInfo(Long.class); public static ClassInfo BOOLEAN = getClassInfo(Boolean.class); public static ClassInfo STRING = getClassInfo(String.class); - - public static ClassInfo WORLD = getClassInfo(World.class); + + // Bukkit + public static ClassInfo OFFLINE_PLAYER = getClassInfo(OfflinePlayer.class); public static ClassInfo LOCATION = getClassInfo(Location.class); public static ClassInfo VECTOR = getClassInfo(Vector.class); - + public static ClassInfo PLAYER = getClassInfo(Player.class); + public static ClassInfo WORLD = getClassInfo(World.class); + + // Skript public static ClassInfo COLOR = getClassInfo(Color.class); public static ClassInfo DATE = getClassInfo(Date.class); public static ClassInfo TIMESPAN = getClassInfo(Timespan.class); - public static ClassInfo OFFLINE_PLAYER = getClassInfo(OfflinePlayer.class); - public static ClassInfo PLAYER = getClassInfo(Player.class); @NotNull - private static ClassInfo getClassInfo(Class tClass) { + private static ClassInfo getClassInfo(Class type) { //noinspection ConstantConditions - ClassInfo classInfo = Classes.getExactClassInfo(tClass); + ClassInfo classInfo = Classes.getExactClassInfo(type); if (classInfo == null) throw new NullPointerException(); return classInfo; } - + } diff --git a/src/main/java/ch/njol/skript/sections/EffSecSpawn.java b/src/main/java/ch/njol/skript/sections/EffSecSpawn.java index 552bfb151b1..7f627f0f750 100644 --- a/src/main/java/ch/njol/skript/sections/EffSecSpawn.java +++ b/src/main/java/ch/njol/skript/sections/EffSecSpawn.java @@ -18,18 +18,6 @@ */ package ch.njol.skript.sections; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import ch.njol.skript.doc.RequiredPlugins; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import ch.njol.skript.Skript; import ch.njol.skript.config.SectionNode; import ch.njol.skript.doc.Description; @@ -47,24 +35,42 @@ import ch.njol.skript.util.Getter; import ch.njol.skript.variables.Variables; import ch.njol.util.Kleenean; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; @Name("Spawn") @Description({ - "Spawn a creature. This can be used as an effect and as a section.", + "Spawns entities. This can be used as an effect and as a section.", + "", "If it is used as a section, the section is run before the entity is added to the world.", "You can modify the entity in this section, using for example 'event-entity' or 'cow'. ", - "Do note that other event values, such as 'player', won't work in this section." + "Do note that other event values, such as 'player', won't work in this section.", + "", + "If you're spawning a display and want it to be empty on initialization, like not having a block display be stone, " + + "set hidden config node 'spawn empty displays' to true." }) @Examples({ "spawn 3 creepers at the targeted block", "spawn a ghast 5 meters above the player", "spawn a zombie at the player:", - "\tset name of the zombie to \"\"" + "\tset name of the zombie to \"\"", + "", + "spawn a block display of a ladder[waterlogged=true] at location above player:", + "\tset billboard of event-display to center # allows the display to rotate around the center axis" }) @Since("1.0, 2.6.1 (with section), 2.8.6 (dropped items)") public class EffSecSpawn extends EffectSection { public static class SpawnEvent extends Event { + private final Entity entity; public SpawnEvent(Entity entity) { @@ -84,21 +90,19 @@ public HandlerList getHandlers() { static { Skript.registerSection(EffSecSpawn.class, - "(spawn|summon) %entitytypes% [%directions% %locations%]", - "(spawn|summon) %number% of %entitytypes% [%directions% %locations%]" + "(spawn|summon) %entitytypes% [%directions% %locations%]", + "(spawn|summon) %number% of %entitytypes% [%directions% %locations%]" ); EventValues.registerEventValue(SpawnEvent.class, Entity.class, new Getter() { @Override - public Entity get(SpawnEvent spawnEvent) { - return spawnEvent.getEntity(); + public Entity get(SpawnEvent event) { + return event.getEntity(); } }, EventValues.TIME_NOW); } - @SuppressWarnings("NotNullFieldNotInitialized") private Expression locations; - @SuppressWarnings("NotNullFieldNotInitialized") private Expression types; @Nullable @@ -112,12 +116,9 @@ public Entity get(SpawnEvent spawnEvent) { @Override @SuppressWarnings("unchecked") - public boolean init(Expression[] exprs, - int matchedPattern, - Kleenean isDelayed, - ParseResult parseResult, - @Nullable SectionNode sectionNode, - @Nullable List triggerItems) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, + @Nullable SectionNode sectionNode, @Nullable List triggerItems) { + amount = matchedPattern == 0 ? null : (Expression) (exprs[0]); types = (Expression) exprs[matchedPattern]; locations = Direction.combine((Expression) exprs[1 + matchedPattern], (Expression) exprs[2 + matchedPattern]); @@ -131,7 +132,6 @@ public boolean init(Expression[] exprs, return false; } } - return true; } @@ -143,9 +143,9 @@ protected TriggerItem walk(Event event) { Consumer consumer; if (trigger != null) { - consumer = o -> { - lastSpawned = o; - SpawnEvent spawnEvent = new SpawnEvent(o); + consumer = entity -> { + lastSpawned = entity; + SpawnEvent spawnEvent = new SpawnEvent(entity); // Copy the local variables from the calling code to this section Variables.setLocalVariables(spawnEvent, Variables.copyLocalVariables(event)); TriggerItem.walk(trigger, spawnEvent); @@ -182,7 +182,7 @@ protected TriggerItem walk(Event event) { @Override public String toString(@Nullable Event event, boolean debug) { return "spawn " + (amount != null ? amount.toString(event, debug) + " of " : "") + - types.toString(event, debug) + " " + locations.toString(event, debug); + types.toString(event, debug) + " " + locations.toString(event, debug); } } diff --git a/src/main/java/ch/njol/skript/util/ColorRGB.java b/src/main/java/ch/njol/skript/util/ColorRGB.java index ea23bfe464a..141f753f701 100644 --- a/src/main/java/ch/njol/skript/util/ColorRGB.java +++ b/src/main/java/ch/njol/skript/util/ColorRGB.java @@ -1,28 +1,14 @@ -/** - * This file is part of Skript. - * - * Skript is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Skript 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Skript. If not, see . - * - * Copyright Peter Güttinger, SkriptLang team and contributors - */ package ch.njol.skript.util; +import ch.njol.skript.Skript; import ch.njol.skript.variables.Variables; import ch.njol.util.Math2; import ch.njol.yggdrasil.Fields; import org.apache.commons.lang.math.NumberUtils; import org.bukkit.DyeColor; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.NotSerializableException; @@ -32,38 +18,100 @@ public class ColorRGB implements Color { + private static final boolean HAS_ARGB = Skript.methodExists(org.bukkit.Color.class, "getAlpha"); private static final Pattern RGB_PATTERN = Pattern.compile("(?>rgb|RGB) (\\d+), (\\d+), (\\d+)"); private org.bukkit.Color bukkit; - @Nullable - private DyeColor dye; - + + private @Nullable DyeColor dye; + + /** + * Subject to being private in the future. Use {@link #fromRGB(int, int, int)} + * This is to keep inline with other color classes. + */ + @Deprecated + @ApiStatus.Internal public ColorRGB(int red, int green, int blue) { - this.bukkit = org.bukkit.Color.fromRGB( + this(org.bukkit.Color.fromRGB( Math2.fit(0, red, 255), Math2.fit(0, green, 255), - Math2.fit(0, blue, 255)); + Math2.fit(0, blue, 255))); + } + + /** + * Subject to being private in the future. Use {@link #fromBukkitColor(org.bukkit.Color)} + * This is to keep inline with other color classes. + */ + @Deprecated + @ApiStatus.Internal + public ColorRGB(org.bukkit.Color bukkit) { this.dye = DyeColor.getByColor(bukkit); + this.bukkit = bukkit; + } + + /** + * Returns a ColorRGB object from the provided arguments. Versions lower than 1.19 will not support alpha values. + * + * @param red red value (0 to 255) + * @param green green value (0 to 255) + * @param blue blue value (0 to 255) + * @param alpha alpha value (0 to 255) + * @return ColorRGB + */ + @Contract("_,_,_,_ -> new") + public static @NotNull ColorRGB fromRGBA(int red, int green, int blue, int alpha) { + org.bukkit.Color bukkit; + if (HAS_ARGB) { + bukkit = org.bukkit.Color.fromARGB(alpha, red, green, blue); + } else { + bukkit = org.bukkit.Color.fromRGB(red, green, blue); + } + return new ColorRGB(bukkit); + } + + /** + * Returns a ColorRGB object from the provided arguments. + * + * @param red red value (0 to 255) + * @param green green value (0 to 255) + * @param blue blue value (0 to 255) + * @return ColorRGB + */ + @Contract("_,_,_ -> new") + public static @NotNull ColorRGB fromRGB(int red, int green, int blue) { + return new ColorRGB(red, green, blue); } - + + /** + * Returns a ColorRGB object from a bukkit color. + * + * @param bukkit the bukkit color to replicate + * @return ColorRGB + */ + @Contract("_ -> new") + public static @NotNull ColorRGB fromBukkitColor(org.bukkit.Color bukkit) { + return new ColorRGB(bukkit); + } + @Override public org.bukkit.Color asBukkitColor() { return bukkit; } - + @Override - @Nullable - public DyeColor asDyeColor() { + public @Nullable DyeColor asDyeColor() { return dye; } - + @Override public String getName() { - return "rgb " + bukkit.getRed() + ", " + bukkit.getGreen() + ", " + bukkit.getBlue(); + String rgb = bukkit.getRed() + ", " + bukkit.getGreen() + ", " + bukkit.getBlue(); + if (HAS_ARGB && bukkit.getAlpha() != 255) + return "argb " + bukkit.getAlpha() + ", " + rgb; + return "rgb " + rgb; } - @Nullable - public static ColorRGB fromString(String string) { + public static @Nullable ColorRGB fromString(String string) { Matcher matcher = RGB_PATTERN.matcher(string); if (!matcher.matches()) return null; @@ -78,7 +126,7 @@ public static ColorRGB fromString(String string) { public Fields serialize() throws NotSerializableException { return new Fields(this, Variables.yggdrasil); } - + @Override public void deserialize(Fields fields) throws StreamCorruptedException, NotSerializableException { org.bukkit.Color b = fields.getObject("bukkit", org.bukkit.Color.class); @@ -91,4 +139,5 @@ public void deserialize(Fields fields) throws StreamCorruptedException, NotSeria dye = d; bukkit = b; } + } diff --git a/src/main/java/ch/njol/skript/util/slot/DisplayEntitySlot.java b/src/main/java/ch/njol/skript/util/slot/DisplayEntitySlot.java new file mode 100644 index 00000000000..c40a3275404 --- /dev/null +++ b/src/main/java/ch/njol/skript/util/slot/DisplayEntitySlot.java @@ -0,0 +1,48 @@ +package ch.njol.skript.util.slot; + +import ch.njol.skript.registrations.Classes; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public class DisplayEntitySlot extends Slot { + + private ItemDisplay display; + + public DisplayEntitySlot(ItemDisplay display) { + this.display = display; + } + + @Override + @Nullable + public ItemStack getItem() { + return display.getItemStack(); + } + + @Override + public void setItem(@Nullable ItemStack item) { + display.setItemStack(item); + } + + @Override + public int getAmount() { + return 1; + } + + @Override + public void setAmount(int amount) {} + + @Override + public boolean isSameSlot(Slot other) { + if (other instanceof DisplayEntitySlot) // Same display + return ((DisplayEntitySlot) other).display.equals(display); + return false; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return Classes.toString(getItem()); + } + +} diff --git a/src/main/java/ch/njol/skript/util/visual/VisualEffects.java b/src/main/java/ch/njol/skript/util/visual/VisualEffects.java index b6aaafe5fbc..2b5f32ef769 100644 --- a/src/main/java/ch/njol/skript/util/visual/VisualEffects.java +++ b/src/main/java/ch/njol/skript/util/visual/VisualEffects.java @@ -227,7 +227,7 @@ private static void registerDataSupplier(String id, BiFunction { int colorValue = (int) (((Number) raw).floatValue() * 255); - ColorRGB color = new ColorRGB(colorValue, 0, 0); + ColorRGB color = ColorRGB.fromRGB(colorValue, 0, 0); return new ParticleOption(color, 1); }); diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayData.java b/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayData.java new file mode 100644 index 00000000000..dda662cf256 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayData.java @@ -0,0 +1,217 @@ +package org.skriptlang.skript.bukkit.displays; + +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.registrations.Classes; +import ch.njol.skript.util.ColorRGB; +import ch.njol.skript.variables.Variables; +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Display; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.TextDisplay; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class DisplayData extends EntityData { + + public static final Color DEFAULT_BACKGROUND_COLOR = ColorRGB.fromRGBA(0, 0, 0, 64).asBukkitColor(); + + static { + EntityData.register(DisplayData.class, "display", Display.class, 0, DisplayType.codeNames); + Variables.yggdrasil.registerSingleClass(DisplayType.class, "DisplayType"); + } + + private enum DisplayType { + + ANY("org.bukkit.entity.Display", "display"), + BLOCK("org.bukkit.entity.BlockDisplay", "block display"), + ITEM("org.bukkit.entity.ItemDisplay", "item display"), + TEXT("org.bukkit.entity.TextDisplay", "text display"); + + @Nullable + private Class displaySubClass; + private final String codeName; + + @SuppressWarnings("unchecked") + DisplayType(String className, String codeName) { + try { + this.displaySubClass = (Class) Class.forName(className); + } catch (ClassNotFoundException ignored) {} + this.codeName = codeName; + } + + @Override + public String toString() { + return codeName; + } + + private static final String[] codeNames; + static { + List codeNamesList = new ArrayList<>(); + for (DisplayType type : values()) { + if (type.displaySubClass != null) + codeNamesList.add(type.codeName); + } + codeNames = codeNamesList.toArray(new String[0]); + } + } + + private DisplayType type = DisplayType.ANY; + + @Nullable + private BlockData blockData; + + @Nullable + private ItemStack item; + + @Nullable + private String text; + + public DisplayData() {} + + public DisplayData(DisplayType type) { + this.type = type; + this.matchedPattern = type.ordinal(); + } + + @Override + @SuppressWarnings("unchecked") + protected boolean init(Literal[] exprs, int matchedPattern, ParseResult parseResult) { + type = DisplayType.values()[matchedPattern]; + // default to 0, use 1 for alternate pattern: %x% display instead of display of %x% + int exprIndex = parseResult.hasTag("alt") ? 1 : 0; + if (exprs.length == 0 || exprs[exprIndex] == null) + return true; + + if (type == DisplayType.BLOCK) { + Object object = ((Literal) exprs[exprIndex]).getSingle(); + if (object instanceof ItemType itemType) { + if (!itemType.hasBlock()) { + Skript.error("A block display must be of a block item. " + Classes.toString(itemType.getMaterial()) + " is not a block. If you want to display an item, use an 'item display'."); + return false; + } + blockData = Bukkit.createBlockData(itemType.getBlockMaterial()); + } else { + blockData = (BlockData) object; + } + } else if (type == DisplayType.ITEM) { + ItemType itemType = ((Literal) exprs[exprIndex]).getSingle(); + if (!itemType.hasItem()) { + Skript.error("An item display must be of a valid item. " + Classes.toString(itemType.getMaterial()) + " is not a valid item. If you want to display a block, use a 'block display'."); + return false; + } + item = itemType.getRandom(); + } + return true; + } + + @Override + protected boolean init(@Nullable Class displayClass, @Nullable Display entity) { + DisplayType[] types = DisplayType.values(); + for (int i = types.length - 1; i >= 0; i--) { + Class display = types[i].displaySubClass; + if (display == null) + continue; + //noinspection ConstantConditions + if (entity == null ? displayClass.isAssignableFrom(display) : display.isInstance(entity)) { + type = types[i]; + if (entity != null) { + switch (type) { + case BLOCK -> blockData = ((BlockDisplay) entity).getBlock(); + case ITEM -> item = ((ItemDisplay) entity).getItemStack(); + case TEXT -> text = ((TextDisplay) entity).getText(); + } + } + return true; + } + } + assert false; + return false; + } + + @Override + public void set(Display entity) { + switch (type) { + case BLOCK -> { + if (blockData != null && entity instanceof BlockDisplay blockDisplay) + blockDisplay.setBlock(blockData); + } + case ITEM -> { + if (item != null && entity instanceof ItemDisplay itemDisplay) + itemDisplay.setItemStack(item); + } + case TEXT -> { + if (text != null && entity instanceof TextDisplay textDisplay) + textDisplay.setText(text); + } + } + } + + @Override + public boolean match(Display entity) { + switch (type) { + case BLOCK -> { + if (!(entity instanceof BlockDisplay blockDisplay)) + return false; + if (blockData != null && !blockDisplay.getBlock().equals(blockData)) + return false; + } + case ITEM -> { + if (!(entity instanceof ItemDisplay itemDisplay)) + return false; + if (item != null && !itemDisplay.getItemStack().isSimilar(item)) + return false; + } + case TEXT -> { + if (!(entity instanceof TextDisplay textDisplay)) + return false; + if (text == null) // all text displays should match a blank one. + return true; + String displayText = textDisplay.getText(); + if (displayText == null) + return false; + return displayText.equals(text); + } + } + return type.displaySubClass != null && type.displaySubClass.isInstance(entity); + } + + @Override + public Class getType() { + return type.displaySubClass != null ? type.displaySubClass : Display.class; + } + + @Override + protected int hashCode_i() { + return type.hashCode(); + } + + @Override + protected boolean equals_i(EntityData obj) { + if (obj instanceof DisplayData other) + return type == other.type; + return false; + } + + @Override + public boolean isSupertypeOf(EntityData entityData) { + if (entityData instanceof DisplayData displayData) + return type == DisplayType.ANY || displayData.type == type; + return Display.class.isAssignableFrom(entityData.getType()); + } + + @Override + public EntityData getSuperType() { + return new DisplayData(DisplayType.ANY); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayModule.java b/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayModule.java new file mode 100644 index 00000000000..024552f3219 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/DisplayModule.java @@ -0,0 +1,54 @@ +package org.skriptlang.skript.bukkit.displays; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.EnumClassInfo; +import ch.njol.skript.classes.data.DefaultChangers; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.registrations.Classes; +import org.bukkit.entity.Display; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.TextDisplay; + +import java.io.IOException; + +public class DisplayModule { + + public static void load() throws IOException { + // abort if no class exists + if (!Skript.classExists("org.bukkit.entity.Display")) + return; + + // load classes (todo: replace with registering methods after regitration api + Skript.getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "displays"); + + // Classes + + Classes.registerClass(new ClassInfo<>(Display.class, "display") + .user("displays?") + .name("Display Entity") + .description("A text, block or item display entity.") + .since("INSERT VERSION") + .defaultExpression(new EventValueExpression<>(Display.class)) + .changer(DefaultChangers.nonLivingEntityChanger)); + + Classes.registerClass(new EnumClassInfo<>(Display.Billboard.class, "billboard", "billboards") + .user("billboards?") + .name("Display Billboard") + .description("Represents the billboard setting of a display.") + .since("INSERT VERSION")); + + Classes.registerClass(new EnumClassInfo<>(TextDisplay.TextAlignment.class, "textalignment", "text alignments") + .user("text ?alignments?") + .name("Display Text Alignment") + .description("Represents the text alignment setting of a text display.") + .since("INSERT VERSION")); + + Classes.registerClass(new EnumClassInfo<>(ItemDisplay.ItemDisplayTransform.class, "itemdisplaytransform", "item display transforms") + .user("item ?display ?transforms?") + .name("Item Display Transforms") + .description("Represents the transform setting of an item display.") + .since("INSERT VERSION")); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBillboard.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBillboard.java new file mode 100644 index 00000000000..8072df69c4e --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBillboard.java @@ -0,0 +1,60 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.Display.Billboard; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Billboard") +@Description({ + "Returns or changes the billboard setting of displays.", + "This describes the axes/points around which the display can pivot.", + "Displays spawn with the 'fixed' billboard by default. Resetting this expression will also set it to 'fixed'." +}) +@Examples("set billboard of the last spawned text display to center") +@Since("INSERT VERSION") +public class ExprDisplayBillboard extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayBillboard.class, Billboard.class, "bill[ |-]board[ing] [setting]", "displays"); + } + + @Override + @Nullable + public Billboard convert(Display display) { + return display.getBillboard(); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case RESET -> CollectionUtils.array(); + case SET -> CollectionUtils.array(Billboard.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Billboard billboard = delta != null ? (Billboard) delta[0] : Billboard.FIXED; + for (Display display : getExpr().getArray(event)) + display.setBillboard(billboard); + } + + @Override + public Class getReturnType() { + return Billboard.class; + } + + @Override + protected String getPropertyName() { + return "billboard"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBrightness.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBrightness.java new file mode 100644 index 00000000000..e9392040f51 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayBrightness.java @@ -0,0 +1,174 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.Display.Brightness; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.util.ArrayList; +import java.util.List; + +@Name("Display Brightness") +@Description({ + "Returns or changes the brightness override of displays.", + "Unmodified displays will not have a brightness override value set. Resetting or deleting this value will remove the override.", + "Use the 'block' or 'sky' options to get/change specific values or get both values as a list by using neither option.", + "NOTE: setting only one of the sky/block light overrides of a display without an existing override will set both sky and block light to the given value. " + + "Make sure to set both block and sky levels to your desired values for the best results. " + + "Likewise, you can only clear the brightness override, you cannot clear/reset the sky/block values individually." +}) +@Examples({ + "set sky light override of the last spawned text display to 7", + "subtract 3 from the block light level override of the last spawned text display", + "if sky light level override of {_display} is 5:", + "\tclear brightness override of {_display}" +}) +@Since("INSERT VERSION") +public class ExprDisplayBrightness extends SimpleExpression { + + static { + Skript.registerExpression(ExprDisplayBrightness.class, Integer.class, ExpressionType.PROPERTY, + "[the] [:block|:sky] (light [level]|brightness) override[s] of %displays%", + "%displays%'[s] [:block|:sky] (light [level]|brightness) override[s]"); + } + + private @UnknownNullability Expression displays; + private boolean blockLight; + private boolean skyLight; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + blockLight = parseResult.hasTag("block"); + skyLight = parseResult.hasTag("sky"); + //noinspection unchecked + displays = (Expression) expressions[0]; + return true; + } + + @Override + protected Integer @Nullable [] get(Event event) { + List values = new ArrayList<>(); + if (skyLight) { + for (Display display : displays.getArray(event)) { + Brightness brightness = display.getBrightness(); + if (brightness == null) + continue; + values.add(brightness.getSkyLight()); + } + } else if (blockLight) { + for (Display display : displays.getArray(event)) { + Brightness brightness = display.getBrightness(); + if (brightness == null) + continue; + values.add(brightness.getBlockLight()); + } + } else { + for (Display display : displays.getArray(event)) { + Brightness brightness = display.getBrightness(); + if (brightness == null) + continue; + values.add(brightness.getBlockLight()); + values.add(brightness.getSkyLight()); + } + } + return values.toArray(new Integer[0]); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + if (skyLight || blockLight) { + return switch (mode) { + case ADD, REMOVE, SET -> CollectionUtils.array(Number.class); + default -> null; + }; + } else { + return switch (mode) { + case SET, RESET, DELETE -> CollectionUtils.array(Number.class); + default -> null; + }; + } + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + if (skyLight || blockLight) { + int level = delta == null ? 0 : ((Number) delta[0]).intValue(); + switch (mode) { + case REMOVE: + level = -level; + // $FALL-THROUGH$ + case ADD: + for (Display display : displays.getArray(event)) { + Brightness brightness = display.getBrightness(); + if (brightness == null) { + int clamped = Math2.fit(0, level, 15); + display.setBrightness(new Brightness(clamped, clamped)); + } else if (skyLight) { + int clamped = Math2.fit(0, level + brightness.getSkyLight(), 15); + display.setBrightness(new Brightness(brightness.getBlockLight(), clamped)); + } else { + int clamped = Math2.fit(0, level + brightness.getBlockLight(), 15); + display.setBrightness(new Brightness(clamped, brightness.getSkyLight())); + } + } + break; + case SET: + for (Display display : displays.getArray(event)) { + Brightness brightness = display.getBrightness(); + int clamped = Math2.fit(0, level, 15); + if (brightness == null) { + display.setBrightness(new Brightness(clamped, clamped)); + } else if (skyLight) { + display.setBrightness(new Brightness(brightness.getBlockLight(), clamped)); + } else { + display.setBrightness(new Brightness(clamped, brightness.getSkyLight())); + } + } + break; + } + } else { + Brightness change = null; + if (delta != null) { + int value = Math2.fit(0, ((Number) delta[0]).intValue(), 15); + change = new Brightness(value, value); + } + for (Display display : displays.getArray(event)) + display.setBrightness(change); + } + } + + @Override + public boolean isSingle() { + return (skyLight || blockLight) && displays.isSingle(); + } + + @Override + public Class getReturnType() { + return Integer.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (skyLight) { + return "sky light override of " + displays.toString(event, debug); + } else if (blockLight) { + return "block light override of " + displays.toString(event, debug); + } else { + return "brightness override of " + displays.toString(event, debug); + } + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayGlowOverride.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayGlowOverride.java new file mode 100644 index 00000000000..84c6bd63723 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayGlowOverride.java @@ -0,0 +1,60 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.Color; +import ch.njol.skript.util.ColorRGB; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Glow Color Override") +@Description({ + "Returns or changes the glowing color override of displays.", + "This overrides whatever color is already set for the scoreboard team of the displays." +}) +@Examples("set glow color override of the last spawned text display to blue") +@Since("INSERT VERSION") +public class ExprDisplayGlowOverride extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayGlowOverride.class, Color.class, "glow[ing] colo[u]r[s] override[s]", "displays"); + } + + @Override + @Nullable + public Color convert(Display display) { + org.bukkit.Color color = display.getGlowColorOverride(); + return color != null ? ColorRGB.fromBukkitColor(color) : null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, RESET, DELETE -> CollectionUtils.array(Color.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + org.bukkit.Color color = delta != null ? ((Color) delta[0]).asBukkitColor() : null; + for (Display display : getExpr().getArray(event)) + display.setGlowColorOverride(color); + } + + @Override + public Class getReturnType() { + return Color.class; + } + + @Override + protected String getPropertyName() { + return "glow color override"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayHeightWidth.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayHeightWidth.java new file mode 100644 index 00000000000..b6f4c0ec75a --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayHeightWidth.java @@ -0,0 +1,105 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Height/Width") +@Description({ + "Returns or changes the height or width of displays.", + "The rendering culling bounding box spans horizontally width/2 from entity position, " + + "which determines the point at which the display will be frustum culled (no longer rendered because the game " + + "determines you are no longer able to see it).", + "If set to 0, no culling will occur on both the vertical and horizontal directions. Default is 0.0." +}) +@Examples("set display height of the last spawned text display to 2.5") +@Since("INSERT VERSION") +public class ExprDisplayHeightWidth extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayHeightWidth.class, Float.class, "display (:height|width)", "displays"); + } + + private boolean height; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + height = parseResult.hasTag("height"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + @Nullable + public Float convert(Display display) { + return height ? display.getDisplayHeight() : display.getDisplayWidth(); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, RESET, SET -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + + float change = delta == null ? 0F : ((Number) delta[0]).floatValue(); + if (Float.isInfinite(change) || Float.isNaN(change)) + return; + + switch (mode) { + case REMOVE: + change = -change; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + if (height) { + float value = Math.max(0F, display.getDisplayHeight() + change); + if (Float.isInfinite(value)) + continue; + display.setDisplayHeight(value); + } else { + float value = Math.max(0F, display.getDisplayWidth() + change); + if (Float.isInfinite(value)) + continue; + display.setDisplayWidth(value); + } + } + break; + case RESET: + case SET: + change = Math.max(0F, change); + for (Display display : displays) { + if (height) { + display.setDisplayHeight(change); + } else { + display.setDisplayWidth(change); + } + } + break; + } + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return height ? "height" : "width"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayInterpolation.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayInterpolation.java new file mode 100644 index 00000000000..5f1b45f2b13 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayInterpolation.java @@ -0,0 +1,104 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; +import ch.njol.util.Kleenean; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Interpolation Delay/Duration") +@Description({ + "Returns or changes the interpolation delay/duration of displays.", + "Interpolation duration is the amount of time a display will take to interpolate, or shift, between its current state and a new state.", + "Interpolation delay is the amount of ticks before client-side interpolation will commence." + + "Setting to 0 seconds will make it immediate.", + "Resetting either value will return that value to 0." +}) +@Examples("set interpolation delay of the last spawned text display to 2 ticks") +@Since("INSERT VERSION") +public class ExprDisplayInterpolation extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayInterpolation.class, Timespan.class, "interpolation (:delay|duration)[s]", "displays"); + } + + private boolean delay; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + delay = parseResult.hasTag("delay"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + @Nullable + public Timespan convert(Display display) { + return new Timespan(Timespan.TimePeriod.TICK, delay ? display.getInterpolationDelay() : display.getInterpolationDuration()); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, SET -> CollectionUtils.array(Timespan.class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + long ticks = 0; + if (delta != null) + ticks = Math2.fit(Integer.MIN_VALUE, ((Timespan) delta[0]).getAs(TimePeriod.TICK), Integer.MAX_VALUE); + + switch (mode) { + case REMOVE: + ticks = -ticks; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + if (delay) { + int value = (int) Math2.fit(0, display.getInterpolationDelay() + ticks, Integer.MAX_VALUE); + display.setInterpolationDelay(value); + } else { + int value = (int) Math2.fit(0, display.getInterpolationDuration() + ticks, Integer.MAX_VALUE); + display.setInterpolationDuration(value); + } + } + break; + case RESET: + case SET: + ticks = Math2.fit(0, ticks, Integer.MAX_VALUE); + for (Display display : displays) { + if (delay) { + display.setInterpolationDelay((int) ticks); + } else { + display.setInterpolationDuration((int) ticks); + } + } + break; + } + } + + @Override + public Class getReturnType() { + return Timespan.class; + } + + @Override + protected String getPropertyName() { + return "interpolation " + (delay ? "delay" : "duration"); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayShadow.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayShadow.java new file mode 100644 index 00000000000..21b09cad242 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayShadow.java @@ -0,0 +1,100 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Shadow Radius/Strength") +@Description("Returns or changes the shadow radius/strength of displays.") +@Examples("set shadow radius of the last spawned text display to 1.75") +@Since("INSERT VERSION") +public class ExprDisplayShadow extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayShadow.class, Float.class, "shadow (:radius|strength)", "displays"); + } + + private boolean radius; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + radius = parseResult.hasTag("radius"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + @Nullable + public Float convert(Display display) { + return radius ? display.getShadowRadius() : display.getShadowStrength(); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, SET, REMOVE -> CollectionUtils.array(Number.class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + float change = delta == null ? 0F : ((Number) delta[0]).floatValue(); + if (Float.isInfinite(change) || Float.isNaN(change)) + return; + switch (mode) { + case REMOVE: + change = -change; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + if (radius) { + float value = Math.max(0F, display.getShadowRadius() + change); + if (Float.isInfinite(value)) + continue; + display.setShadowRadius(value); + } else { + float value = Math.max(0F, display.getShadowStrength() + change); + if (Float.isInfinite(value)) + continue; + display.setShadowStrength(value); + } + } + break; + case RESET: + if (!radius) + change = 1; // default strength is 1 + case SET: + change = Math.max(0F, change); + for (Display display : displays) { + if (radius) { + display.setShadowRadius(change); + } else { + display.setShadowStrength(change); + } + } + break; + } + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return radius ? "radius" : "strength"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTeleportDuration.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTeleportDuration.java new file mode 100644 index 00000000000..951bf727d88 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTeleportDuration.java @@ -0,0 +1,88 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display Teleport Delay/Duration") +@Description({ + "The teleport duration of displays is the amount of time it takes to get between locations.", + "0 means that updates are applied immediately.", + "1 means that the display entity will move from current position to the updated one over one tick.", + "Higher values spread the movement over multiple ticks. Max of 59 ticks." +}) +@Examples({ + "set teleport delay of the last spawned text display to 2 ticks", + "teleport last spawned text display to {_location}", + "wait 2 ticks", + "message \"display entity has arrived at %{_location}%\"" +}) +@RequiredPlugins("Spigot 1.20.4+") +@Since("INSERT VERSION") +public class ExprDisplayTeleportDuration extends SimplePropertyExpression { + + static { + if (Skript.isRunningMinecraft(1, 20, 4)) + registerDefault(ExprDisplayTeleportDuration.class, Timespan.class, "teleport[ation] duration[s]", "displays"); + } + + @Override + @Nullable + public Timespan convert(Display display) { + return new Timespan(TimePeriod.TICK, display.getTeleportDuration()); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, SET -> CollectionUtils.array(Timespan.class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + long ticks = (delta == null ? 0 : ((Timespan) delta[0]).getAs(TimePeriod.TICK)); + switch (mode) { + case REMOVE: + ticks = -ticks; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + int value = (int) Math2.fit(0, display.getTeleportDuration() + ticks, 59); + display.setTeleportDuration(value); + } + break; + case RESET: + case SET: + ticks = Math2.fit(0, ticks, 59); + for (Display display : displays) + display.setTeleportDuration((int) ticks); + break; + } + } + + @Override + public Class getReturnType() { + return Timespan.class; + } + + @Override + protected String getPropertyName() { + return "teleport duration"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationRotation.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationRotation.java new file mode 100644 index 00000000000..cb7a78c7e0b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationRotation.java @@ -0,0 +1,85 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.bukkit.util.Transformation; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; + +@Name("Display Transformation Rotation") +@Description({ + "Returns or changes the transformation rotation of displays.", + "The left rotation is applied first, with the right rotation then being applied based on the rotated axis." +}) +@Examples("set left transformation rotation of last spawned block display to quaternion(1, 0, 0, 0) # reset block display") +@Since("INSERT VERSION") +public class ExprDisplayTransformationRotation extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayTransformationRotation.class, Quaternionf.class, "(:left|right) [transformation] rotation", "displays"); + } + + private boolean left; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + left = parseResult.hasTag("left"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + public @Nullable Quaternionf convert(Display display) { + Transformation transformation = display.getTransformation(); + return left ? transformation.getLeftRotation() : transformation.getRightRotation(); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, RESET -> CollectionUtils.array(Quaternionf.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Quaternionf quaternion = null; + if (mode == ChangeMode.RESET) + quaternion = new Quaternionf(0, 0, 0, 1); + if (delta != null) { + quaternion = (Quaternionf) delta[0]; + } + if (quaternion == null || !quaternion.isFinite()) + return; + for (Display display : getExpr().getArray(event)) { + Transformation transformation = display.getTransformation(); + Transformation change; + if (left) { + change = new Transformation(transformation.getTranslation(), quaternion, transformation.getScale(), transformation.getRightRotation()); + } else { + change = new Transformation(transformation.getTranslation(), transformation.getLeftRotation(), transformation.getScale(), quaternion); + } + display.setTransformation(change); + } + } + + @Override + public Class getReturnType() { + return Quaternionf.class; + } + + @Override + protected String getPropertyName() { + return (left ? "left" : "right") + " transformation rotation"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationScaleTranslation.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationScaleTranslation.java new file mode 100644 index 00000000000..8e1d3ebcd50 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayTransformationScaleTranslation.java @@ -0,0 +1,82 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.bukkit.util.Transformation; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +@Name("Display Transformation Scale/Translation") +@Description("Returns or changes the transformation scale or translation of displays.") +@Examples("set transformation translation of display to vector from -0.5, -0.5, -0.5 # Center the display in the same position as a block") +@Since("INSERT VERSION") +public class ExprDisplayTransformationScaleTranslation extends SimplePropertyExpression { + + static { + register(ExprDisplayTransformationScaleTranslation.class, Vector.class, "(display|[display] transformation) (:scale|translation)", "displays"); + } + + private boolean scale; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + scale = parseResult.hasTag("scale"); + return super.init(exprs, matchedPattern, isDelayed, parseResult); + } + + @Override + public @Nullable Vector convert(Display display) { + Transformation transformation = display.getTransformation(); + return Vector.fromJOML(scale ? transformation.getScale() : transformation.getTranslation()); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, RESET -> CollectionUtils.array(Vector.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Vector3f vector = null; + if (mode == ChangeMode.RESET) + vector = scale ? new Vector3f(1F, 1F, 1F) : new Vector3f(0F, 0F, 0F); + if (delta != null) + vector = ((Vector) delta[0]).toVector3f(); + if (vector == null || !vector.isFinite()) + return; + for (Display display : getExpr().getArray(event)) { + Transformation transformation = display.getTransformation(); + Transformation change; + if (scale) { + change = new Transformation(transformation.getTranslation(), transformation.getLeftRotation(), vector, transformation.getRightRotation()); + } else { + change = new Transformation(vector, transformation.getLeftRotation(), transformation.getScale(), transformation.getRightRotation()); + } + display.setTransformation(change); + } + } + + @Override + public Class getReturnType() { + return Vector.class; + } + + @Override + protected String getPropertyName() { + return "transformation " + (scale ? "scale" : "translation"); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayViewRange.java b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayViewRange.java new file mode 100644 index 00000000000..0775fb4f1fd --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/generic/ExprDisplayViewRange.java @@ -0,0 +1,78 @@ +package org.skriptlang.skript.bukkit.displays.generic; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Display View Range") +@Description({ + "Returns or changes the view range of displays.", + "Default value is 1.0. This value is then multiplied by 64 and the player's entity view distance setting to determine the actual range.", + "For example, a player with 150% entity view distance will see a block display with a view range of 1.2 at 1.2 * 64 * 150% = 115.2 blocks away." +}) +@Examples("set view range of the last spawned text display to 2.9") +@Since("INSERT VERSION") +public class ExprDisplayViewRange extends SimplePropertyExpression { + + static { + registerDefault(ExprDisplayViewRange.class, Float.class, "[display] view (range|radius)", "displays"); + } + + @Override + public @Nullable Float convert(Display display) { + return display.getViewRange(); + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, SET, REMOVE -> CollectionUtils.array(Number.class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + float change = delta == null ? 1F : ((Number) delta[0]).floatValue(); + if (Float.isNaN(change) || Float.isInfinite(change)) + return; + switch (mode) { + case REMOVE: + change = -change; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + float value = Math.max(0F, display.getViewRange() + change); + if (Float.isInfinite(value)) + continue; + display.setViewRange(value); + } + break; + case RESET: + case SET: + change = Math.max(0F, change); + for (Display display : displays) + display.setViewRange(change); + break; + } + } + + @Override + public Class getReturnType() { + return Float.class; + } + + @Override + protected String getPropertyName() { + return "view range"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/item/ExprItemDisplayTransform.java b/src/main/java/org/skriptlang/skript/bukkit/displays/item/ExprItemDisplayTransform.java new file mode 100644 index 00000000000..ea742c826d8 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/item/ExprItemDisplayTransform.java @@ -0,0 +1,64 @@ +package org.skriptlang.skript.bukkit.displays.item; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.ItemDisplay.ItemDisplayTransform; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Item Display Transform") +@Description("Returns or changes the item display transform of item displays.") +@Examples({ + "set the item transform of the last spawned item display to first person left handed", + "set the item transform of the last spawned item display to no transform # Reset to default" +}) +@Since("INSERT VERSION") +public class ExprItemDisplayTransform extends SimplePropertyExpression { + + static { + registerDefault(ExprItemDisplayTransform.class, ItemDisplayTransform.class, "item [display] transform", "displays"); + } + + @Override + public @Nullable ItemDisplayTransform convert(Display display) { + if (display instanceof ItemDisplay itemDisplay) + return itemDisplay.getItemDisplayTransform(); + return null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case RESET -> CollectionUtils.array(); + case SET -> CollectionUtils.array(ItemDisplayTransform.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + //noinspection ConstantConditions + ItemDisplayTransform transform = mode == ChangeMode.SET ? (ItemDisplayTransform) delta[0] : ItemDisplayTransform.NONE; + for (Display display : getExpr().getArray(event)) { + if (display instanceof ItemDisplay itemDisplay) + itemDisplay.setItemDisplayTransform(transform); + } + } + + @Override + public Class getReturnType() { + return ItemDisplayTransform.class; + } + + @Override + protected String getPropertyName() { + return "item display transform"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplayHasDropShadow.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplayHasDropShadow.java new file mode 100644 index 00000000000..919fe9e3c09 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplayHasDropShadow.java @@ -0,0 +1,56 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.Skript; +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; + +@Name("Text Display Has Drop Shadow") +@Description("Returns whether the text of a display has drop shadow applied.") +@Examples({ + "if {_display} has drop shadow:", + "\tremove drop shadow from the text of {_display}" +}) +@Since("INSERT VERSION") +public class CondTextDisplayHasDropShadow extends PropertyCondition { + + static { + Skript.registerCondition(CondTextDisplayHasDropShadow.class, + "[[the] text of] %displays% (has|have) [a] (drop|text) shadow", + "%displays%'[s] text (has|have) [a] (drop|text) shadow", + "[[the] text of] %displays% (doesn't|does not|do not|don't) have [a] (drop|text) shadow", + "%displays%'[s] text (doesn't|does not|do not|don't) have [a] (drop|text) shadow" + ); + } + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + if (!super.init(expressions, matchedPattern, isDelayed, parseResult)) + return false; + setNegated(matchedPattern > 1); + return true; + } + + @Override + public boolean check(Display value) { + return value instanceof TextDisplay textDisplay && textDisplay.isShadowed(); + } + + @Override + protected PropertyType getPropertyType() { + return PropertyType.HAVE; + } + + @Override + protected String getPropertyName() { + return "drop shadow"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplaySeeThroughBlocks.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplaySeeThroughBlocks.java new file mode 100644 index 00000000000..6e6cee28d1b --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/CondTextDisplaySeeThroughBlocks.java @@ -0,0 +1,34 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.conditions.base.PropertyCondition; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; + +@Name("Text Display Visible Through Blocks") +@Description("Returns whether text displays can be seen through blocks or not.") +@Examples({ + "if last spawned text display is visible through walls:", + "\tprevent last spawned text display from being visible through walls" +}) +@Since("INSERT VERSION") +public class CondTextDisplaySeeThroughBlocks extends PropertyCondition { + + static { + register(CondTextDisplaySeeThroughBlocks.class, "visible through (blocks|walls)", "displays"); + } + + @Override + public boolean check(Display value) { + return value instanceof TextDisplay textDisplay && textDisplay.isSeeThrough(); + } + + @Override + protected String getPropertyName() { + return "visible through blocks"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplayDropShadow.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplayDropShadow.java new file mode 100644 index 00000000000..3545faf4e11 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplayDropShadow.java @@ -0,0 +1,62 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Text Display Drop Shadow") +@Description("Applies or removes drop shadow from the displayed text on a text display.") +@Examples({ + "apply drop shadow to last spawned text display", + "if {_display} has drop shadow:", + "\tremove drop shadow from the text of {_display}" +}) +@Since("INSERT VERSION") +public class EffTextDisplayDropShadow extends Effect { + + static { + Skript.registerEffect(EffTextDisplayDropShadow.class, + "(apply|add) (drop|text) shadow to [[the] text of] %displays%", + "(apply|add) (drop|text) shadow to %displays%'[s] text", + "(remove|clear) (drop|text) shadow from [[the] text of] %displays%", + "(remove|clear) (drop|text) shadow from %displays%'[s] text" + ); + } + + private Expression displays; + private boolean addShadow; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + displays = (Expression) expressions[0]; + addShadow = matchedPattern <= 1; + return true; + } + + @Override + protected void execute(Event event) { + for (Display display : displays.getArray(event)) { + if (display instanceof TextDisplay textDisplay) + textDisplay.setShadowed(addShadow); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (addShadow) + return "add drop shadow to " + displays.toString(event, debug); + return "remove drop shadow from " + displays.toString(event, debug); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplaySeeThroughBlocks.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplaySeeThroughBlocks.java new file mode 100644 index 00000000000..9ed947ed746 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/EffTextDisplaySeeThroughBlocks.java @@ -0,0 +1,60 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Text Display See Through Blocks") +@Description("Forces a text display to either be or not be visible through blocks.") +@Examples({ + "force last spawned text display to be visible through walls", + "prevent all text displays from being visible through walls" +}) +@Since("INSERT VERSION") +public class EffTextDisplaySeeThroughBlocks extends Effect { + + static { + Skript.registerEffect(EffTextDisplaySeeThroughBlocks.class, + "make %displays% visible through (blocks|walls)", + "force %displays% to be visible through (blocks|walls)", + "(prevent|block) %displays% from being (visible|seen) through (blocks|walls)" + ); + } + + Expression displays; + boolean canSee; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + //noinspection unchecked + displays = (Expression) expressions[0]; + canSee = matchedPattern != 2; + return true; + } + + @Override + protected void execute(Event event) { + for (Display display : displays.getArray(event)) { + if (display instanceof TextDisplay textDisplay) + textDisplay.setSeeThrough(canSee); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + if (canSee) + return "force " + displays.toString(event, debug) + " to be visible through blocks"; + return "prevent " + displays.toString(event, debug) + " from being visible through blocks"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayAlignment.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayAlignment.java new file mode 100644 index 00000000000..f123d3aa3c1 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayAlignment.java @@ -0,0 +1,61 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; +import org.bukkit.entity.TextDisplay.TextAlignment; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Text Display Alignment") +@Description("Returns or changes the alignment setting of text displays.") +@Examples("set text alignment of the last spawned text display to left") +@Since("INSERT VERSION") +public class ExprTextDisplayAlignment extends SimplePropertyExpression { + + static { + registerDefault(ExprTextDisplayAlignment.class, TextAlignment.class, "text alignment[s]", "displays"); + } + + @Override + public @Nullable TextAlignment convert(Display display) { + if (display instanceof TextDisplay textDisplay) + return textDisplay.getAlignment(); + return null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case RESET -> CollectionUtils.array(); + case SET -> CollectionUtils.array(TextAlignment.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + //noinspection ConstantConditions + TextAlignment alignment = mode == ChangeMode.RESET ? TextAlignment.CENTER : (TextAlignment) delta[0]; + for (Display display : getExpr().getArray(event)) { + if (display instanceof TextDisplay textDisplay) + textDisplay.setAlignment(alignment); + } + } + + @Override + public Class getReturnType() { + return TextAlignment.class; + } + + @Override + protected String getPropertyName() { + return "text alignment"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayLineWidth.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayLineWidth.java new file mode 100644 index 00000000000..3ac44924046 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayLineWidth.java @@ -0,0 +1,77 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Text Display Line Width") +@Description("Returns or changes the line width of text displays. Default is 200.") +@Examples("set the line width of the last spawned text display to 300") +@Since("INSERT VERSION") +public class ExprTextDisplayLineWidth extends SimplePropertyExpression { + + static { + registerDefault(ExprTextDisplayLineWidth.class, Integer.class, "line width", "displays"); + } + + @Override + public @Nullable Integer convert(Display display) { + if (display instanceof TextDisplay textDisplay) + return textDisplay.getLineWidth(); + return null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, SET -> CollectionUtils.array(Number.class); + case RESET -> CollectionUtils.array(); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + int change = delta == null ? 200 : ((Number) delta[0]).intValue(); + switch (mode) { + case REMOVE: + change = -change; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + if (display instanceof TextDisplay textDisplay) { + int value = Math.max(0, textDisplay.getLineWidth() + change); + textDisplay.setLineWidth(value); + } + } + break; + case RESET: + case SET: + change = Math.max(0, change); + for (Display display : displays) { + if (display instanceof TextDisplay textDisplay) + textDisplay.setLineWidth(change); + } + break; + } + } + + @Override + public Class getReturnType() { + return Integer.class; + } + + @Override + protected String getPropertyName() { + return "line width"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayOpacity.java b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayOpacity.java new file mode 100644 index 00000000000..6cbb541de08 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/displays/text/ExprTextDisplayOpacity.java @@ -0,0 +1,82 @@ +package org.skriptlang.skript.bukkit.displays.text; + +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.Math2; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.Display; +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +@Name("Text Display Opacity") +@Description({ + "Returns or changes the opacity of text displays.", + "Values are between -127 and 127. The value of 127 represents it being completely opaque." +}) +@Examples("set the opacity of the last spawned text display to -1 # Reset") +@Since("INSERT VERSION") +public class ExprTextDisplayOpacity extends SimplePropertyExpression { + + static { + registerDefault(ExprTextDisplayOpacity.class, Byte.class, "[display] opacity", "displays"); + } + + @Override + public @Nullable Byte convert(Display display) { + if (display instanceof TextDisplay textDisplay) + return textDisplay.getTextOpacity(); + return null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, REMOVE, RESET, SET -> CollectionUtils.array(Number.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + Display[] displays = getExpr().getArray(event); + int change = delta == null ? -1 : ((Number) delta[0]).intValue(); + switch (mode) { + case REMOVE_ALL: + case REMOVE: + change = -change; + //$FALL-THROUGH$ + case ADD: + for (Display display : displays) { + if (display instanceof TextDisplay textDisplay) { + byte value = (byte) Math2.fit(-127, textDisplay.getTextOpacity() + change, 127); + textDisplay.setTextOpacity(value); + } + } + break; + case DELETE: + case RESET: + case SET: + change = Math2.fit(-127, change, 127); + for (Display display : displays) { + if (display instanceof TextDisplay textDisplay) + textDisplay.setTextOpacity((byte) change); + } + break; + } + } + + @Override + public Class getReturnType() { + return Byte.class; + } + + @Override + protected String getPropertyName() { + return "opacity"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/effects/EffRotate.java b/src/main/java/org/skriptlang/skript/bukkit/misc/effects/EffRotate.java new file mode 100644 index 00000000000..14ed2fc1737 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/effects/EffRotate.java @@ -0,0 +1,185 @@ +package org.skriptlang.skript.bukkit.misc.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.entity.Display; +import org.bukkit.event.Event; +import org.bukkit.util.Transformation; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.skriptlang.skript.bukkit.misc.rotation.DisplayRotator; +import org.skriptlang.skript.bukkit.misc.rotation.QuaternionRotator; +import org.skriptlang.skript.bukkit.misc.rotation.Rotator; +import org.skriptlang.skript.bukkit.misc.rotation.Rotator.Axis; +import org.skriptlang.skript.bukkit.misc.rotation.VectorRotator; + +import java.util.Locale; + +@Name("Rotate") +@Description({ + "Rotates displays, quaternions, or vectors around an axis a set amount of degrees, or around all 3 axes at once.", + "Vectors can only be rotated around the global X/Y/Z axes, or an arbitrary vector axis.", + "Quaternions are more flexible, allowing rotation around the global or local X/Y/Z axes, arbitrary vectors, or all 3 local axes at once.", + "Global axes are the ones in the Minecraft world. Local axes are relative to how the quaternion is already oriented.", + "", + "Rotating a display is a shortcut for rotating its left rotation. If the right rotation needs to be modified, it should be acquired, rotated, and re-set.", + "", + "Note that rotating a quaternion/display around a vector results in a rotation around the local vector, so results may not be what you expect. " + + "For example, rotating quaternions/displays around vector(1, 0, 0) is the same as rotating around the local X axis.", + "The same applies to rotations by all three axes at once. " + + "In addition, rotating around all three axes of a quaternion/display at once will rotate in ZYX order, meaning the Z rotation will be applied first and the X rotation last." +}) +@Examples({ + "rotate {_quaternion} around x axis by 10 degrees", + "rotate last spawned block display around y axis by 10 degrees", + "rotate {_vector} around vector(1, 1, 1) by 45", + "rotate {_quaternion} by x 45, y 90, z 135" +}) +@Since("2.2-dev28, INSERT VERSION (quaternions, displays)") +public class EffRotate extends Effect { + + static { + Skript.registerEffect(EffRotate.class, + "rotate %vectors/quaternions/displays% around [the] [global] (:x|:y|:z)(-| )axis by %number% [degrees]", + "rotate %quaternions/displays% around [the|its|their] local (:x|:y|:z)(-| )ax(i|e)s by %number% [degrees]", + "rotate %vectors/quaternions/displays% around [the] %vector% by %number% [degrees]", + "rotate %quaternions/displays% by x %number%, y %number%(, [and]| and) z %number%" + ); + } + + private Expression toRotate; + + private @UnknownNullability Expression angle; + private @UnknownNullability Expression vector; + private @UnknownNullability Axis axis; + + private @UnknownNullability Expression x, y, z; + + private int matchedPattern; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + toRotate = exprs[0]; + this.matchedPattern = matchedPattern; + switch (matchedPattern) { + case 0, 1 -> { + String axisString = parseResult.tags.get(0).toUpperCase(Locale.ENGLISH); + if (matchedPattern == 1) + axisString = "LOCAL_" + axisString; + angle = (Expression) exprs[1]; + axis = Axis.valueOf(axisString); + } + case 2 -> { + vector = (Expression) exprs[1]; + angle = (Expression) exprs[2]; + axis = Axis.ARBITRARY; + } + case 3 -> { + x = (Expression) exprs[1]; + y = (Expression) exprs[2]; + z = (Expression) exprs[3]; + } + } + return true; + } + + @Override + protected void execute(Event event) { + if (matchedPattern == 3) { + Number x = this.x.getSingle(event); + Number y = this.y.getSingle(event); + Number z = this.z.getSingle(event); + if (x == null || y == null || z == null) + return; + + float radX = (float) (x.floatValue() * Math.PI / 180); + float radY = (float) (y.floatValue() * Math.PI / 180); + float radZ = (float) (z.floatValue() * Math.PI / 180); + + for (Object object : toRotate.getArray(event)) { + if (object instanceof Quaternionf quaternion) { + quaternion.rotateZYX(radZ, radY, radX); + } else if (object instanceof Display display) { + Transformation transformation = display.getTransformation(); + Quaternionf leftRotation = transformation.getLeftRotation(); + display.setTransformation( + new Transformation( + transformation.getTranslation(), + leftRotation.rotateZYX(radZ, radY, radX), + transformation.getScale(), + transformation.getRightRotation() + ) + ); + } + } + return; + } + + // rotate around axis + Number angle = this.angle.getSingle(event); + if (angle == null) + return; + double radAngle = (angle.doubleValue() * Math.PI / 180); + if (Double.isInfinite(radAngle) || Double.isNaN(radAngle)) + return; + + Rotator vectorRotator; + Rotator quaternionRotator; + Rotator displayRotator; + + if (axis == Axis.ARBITRARY) { + // rotate around arbitrary axis + Vector axis = vector.getSingle(event); + if (axis == null || axis.isZero()) + return; + axis.normalize(); + Vector3f jomlAxis = axis.toVector3f(); + vectorRotator = new VectorRotator(Axis.ARBITRARY, axis, radAngle); + quaternionRotator = new QuaternionRotator(Axis.LOCAL_ARBITRARY, jomlAxis, (float) radAngle); + displayRotator = new DisplayRotator(Axis.LOCAL_ARBITRARY, jomlAxis, (float) radAngle); + } else { + vectorRotator = new VectorRotator(axis, radAngle); + quaternionRotator = new QuaternionRotator(axis, (float) radAngle); + displayRotator = new DisplayRotator(axis, (float) radAngle); + } + + for (Object object : toRotate.getArray(event)) { + if (object instanceof Vector vectorToRotate) { + vectorRotator.rotate(vectorToRotate); + } else if (object instanceof Quaternionf quaternion) { + quaternionRotator.rotate(quaternion); + } else if (object instanceof Display display) { + displayRotator.rotate(display); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return switch (matchedPattern) { + case 0, 1 -> "rotate " + toRotate.toString(event, debug) + + " around the " + axis + "-axis " + + "by " + angle.toString(event, debug) + " degrees"; + case 2 -> "rotate " + toRotate.toString(event, debug) + + " around " + vector.toString(event, debug) + "-axis " + + "by " + angle.toString(event, debug) + " degrees"; + case 3 -> "rotate " + toRotate.toString(event, debug) + + " by x " + x.toString(event, debug) + ", " + + "y " + y.toString(event, debug) + ", " + + "and z " + z.toString(event, debug); + default -> "invalid"; + }; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprItemOfEntity.java b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprItemOfEntity.java new file mode 100644 index 00000000000..5ec794ad778 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprItemOfEntity.java @@ -0,0 +1,64 @@ +package org.skriptlang.skript.bukkit.misc.expressions; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.slot.DisplayEntitySlot; +import ch.njol.skript.util.slot.DroppedItemSlot; +import ch.njol.skript.util.slot.ItemFrameSlot; +import ch.njol.skript.util.slot.Slot; +import ch.njol.skript.util.slot.ThrowableProjectileSlot; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.ThrowableProjectile; +import org.jetbrains.annotations.Nullable; + +@Name("Item of an Entity") +@Description({ + "An item associated with an entity. For dropped item entities, it gets the item that was dropped.", + "For item frames, the item inside the frame is returned.", + "For throwable projectiles (snowballs, enderpearls etc.) or item displays, it gets the displayed item.", + "Other entities do not have items associated with them." +}) +@Examples({ + "item of event-entity", + "", + "set the item inside of event-entity to a diamond sword named \"Example\"" +}) +@Since("2.2-dev35, 2.2-dev36 (improved), 2.5.2 (throwable projectiles), INSERT VERSION (item displays)") +public class ExprItemOfEntity extends SimplePropertyExpression { + + + static { + register(ExprItemOfEntity.class, Slot.class, "item [inside]", "entities"); + } + + @Override + public @Nullable Slot convert(Entity entity) { + if (entity instanceof ItemFrame itemFrame) { + return new ItemFrameSlot(itemFrame); + } else if (entity instanceof Item item) { + return new DroppedItemSlot(item); + } else if (entity instanceof ThrowableProjectile throwableProjectile) { + return new ThrowableProjectileSlot(throwableProjectile); + } else if (entity instanceof ItemDisplay itemDisplay) { + return new DisplayEntitySlot(itemDisplay); + } + return null; // Other entities don't have associated items + } + + @Override + public Class getReturnType() { + return Slot.class; + } + + @Override + protected String getPropertyName() { + return "item inside"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprQuaternionAxisAngle.java b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprQuaternionAxisAngle.java new file mode 100644 index 00000000000..d74cfabf897 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprQuaternionAxisAngle.java @@ -0,0 +1,104 @@ +package org.skriptlang.skript.bukkit.misc.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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 org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.joml.AxisAngle4f; +import org.joml.Math; +import org.joml.Quaternionf; + +@Name("Rotation Axis/Angle") +@Description({ + "Returns the axis or angle that a quaternion will rotate by/around.", + "All quaternions can be represented by a rotation of some amount around some axis, so this expression provides " + + "the ability to get that angle/axis." +}) +@Examples({ + "set {_quaternion} to axisAngle(45, vector(1, 2, 3))", + "send rotation axis of {_quaternion} # 1, 2, 3", + "send rotation angle of {_quaternion} # 45", + "set rotation angle of {_quaternion} to 135", + "set rotation axis of {_quaternion} to vector(0, 1, 0)" +}) +@Since("INSERT VERSION") +public class ExprQuaternionAxisAngle extends SimplePropertyExpression { + + static { + if (Skript.classExists("org.joml.Quaternionf")) + register(ExprQuaternionAxisAngle.class, Object.class, "rotation (angle|:axis)", "quaternions"); + } + + private boolean isAxis; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + isAxis = parseResult.hasTag("axis"); + return super.init(expressions, matchedPattern, isDelayed, parseResult); + } + + @Override + public @Nullable Object convert(Quaternionf from) { + AxisAngle4f axisAngle = new AxisAngle4f(); + axisAngle.set(from); + if (isAxis) + return new Vector(axisAngle.x, axisAngle.y, axisAngle.z); + return (float) (axisAngle.angle * 180 / Math.PI); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case ADD, SET, REMOVE -> { + if (Changer.ChangerUtils.acceptsChange(getExpr(), ChangeMode.SET, Quaternionf.class)) + yield CollectionUtils.array(isAxis ? Vector.class : Number.class); + yield null; + } + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + assert delta != null; // reset/delete not supported + Quaternionf[] quaternions = getExpr().getArray(event); + AxisAngle4f axisAngle = new AxisAngle4f(); + if (isAxis && delta[0] instanceof Vector vector) { + for (Quaternionf quaternion : quaternions) { + axisAngle.set(quaternion); + axisAngle.set(axisAngle.angle, (float) vector.getX(), (float) vector.getY(), (float) vector.getZ()); + quaternion.set(axisAngle); + } + } else if (delta[0] instanceof Number number) { + float angle = (float) (number.floatValue() / 180 * Math.PI); + for (Quaternionf quaternion : quaternions) { + axisAngle.set(quaternion); + axisAngle.set(angle, axisAngle.x, axisAngle.y, axisAngle.z); + quaternion.set(axisAngle); + } + } + getExpr().change(event, quaternions, ChangeMode.SET); + } + + @Override + public Class getReturnType() { + return isAxis ? Vector.class : Float.class; + } + + @Override + protected String getPropertyName() { + return isAxis ? "axis" : "angle"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprRotate.java b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprRotate.java new file mode 100644 index 00000000000..ee60bf91907 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprRotate.java @@ -0,0 +1,177 @@ +package org.skriptlang.skript.bukkit.misc.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.skriptlang.skript.bukkit.misc.rotation.NonMutatingQuaternionRotator; +import org.skriptlang.skript.bukkit.misc.rotation.NonMutatingVectorRotator; +import org.skriptlang.skript.bukkit.misc.rotation.Rotator; +import org.skriptlang.skript.bukkit.misc.rotation.Rotator.Axis; + +import java.util.Locale; +import java.util.Objects; + +@Name("Rotated Quaternion/Vector") +@Description({ + "Rotates a quaternion or vector around an axis a set amount of degrees, or around all 3 axes at once.", + "Vectors can only be rotated around the global X/Y/Z axes, or an arbitrary vector axis.", + "Quaternions are more flexible, allowing rotation around the global or local X/Y/Z axes, arbitrary vectors, or all 3 local axes at once.", + "Global axes are the ones in the Minecraft world. Local axes are relative to how the quaternion is already oriented.", + "", + "Note that rotating a quaternion around a vector results in a rotation around the local vector, so results may not be what you expect. " + + "For example, rotating around vector(1, 0, 0) is the same as rotating around the local X axis.", + "The same applies to rotations by all three axes at once. " + + "In addition, rotating around all three axes of a quaternion/display at once will rotate in ZYX order, meaning the Z rotation will be applied first and the X rotation last." +}) +@Examples({ + "set {_new} to {_quaternion} rotated around x axis by 10 degrees", + "set {_new} to {_vector} rotated around vector(1, 1, 1) by 45", + "set {_new} to {_quaternion} rotated by x 45, y 90, z 135" +}) +@Since("INSERT VERSION") +public class ExprRotate extends SimpleExpression { + + static { + Skript.registerExpression(ExprRotate.class, Object.class, ExpressionType.SIMPLE, + "%quaternions/vectors% rotated around [the] [global] (:x|:y|:z)(-| )axis by %number% [degrees]", + "%quaternions% rotated around [the|its|their] local (:x|:y|:z)(-| )ax(i|e)s by %number% [degrees]", + "%quaternions/vectors% rotated around [the] %vector% by %number% [degrees]", + "%quaternions% rotated by x %number%, y %number%(, [and]| and) z %number%"); + } + + private Expression toRotate; + + private @UnknownNullability Expression angle; + private @UnknownNullability Expression vector; + private @UnknownNullability Axis axis; + + private @UnknownNullability Expression x, y, z; + + private int matchedPattern; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + toRotate = exprs[0]; + this.matchedPattern = matchedPattern; + switch (matchedPattern) { + case 0, 1 -> { + String axisString = parseResult.tags.get(0).toUpperCase(Locale.ENGLISH); + if (matchedPattern == 1) + axisString = "LOCAL_" + axisString; + angle = (Expression) exprs[1]; + axis = Axis.valueOf(axisString); + } + case 2 -> { + vector = (Expression) exprs[1]; + angle = (Expression) exprs[2]; + axis = Axis.ARBITRARY; + } + case 3 -> { + x = (Expression) exprs[1]; + y = (Expression) exprs[2]; + z = (Expression) exprs[3]; + } + } + return true; + } + + @Override + @Nullable + protected Object[] get(Event event) { + if (matchedPattern == 3) { + Number x = this.x.getSingle(event); + Number y = this.y.getSingle(event); + Number z = this.z.getSingle(event); + if (x == null || y == null || z == null) + return new Quaternionf[0]; + + float radX = (float) (x.floatValue() * Math.PI / 180); + float radY = (float) (y.floatValue() * Math.PI / 180); + float radZ = (float) (z.floatValue() * Math.PI / 180); + + //noinspection unchecked + return ((Expression) toRotate).stream(event) + .map(quaternion -> quaternion.rotateZYX(radZ, radY, radX)) + .toArray(Quaternionf[]::new); + } + + // rotate around axis + Number angle = this.angle.getSingle(event); + if (angle == null) + return new Object[0]; + double radAngle = (angle.doubleValue() * Math.PI / 180); + if (Double.isInfinite(radAngle) || Double.isNaN(radAngle)) + return new Object[0]; + + Rotator vectorRotator; + Rotator quaternionRotator; + + if (axis == Axis.ARBITRARY) { + // rotate around arbitrary axis + Vector axis = vector.getSingle(event); + if (axis == null || axis.isZero()) + return new Object[0]; + axis.normalize(); + Vector3f jomlAxis = axis.toVector3f(); + vectorRotator = new NonMutatingVectorRotator(Axis.ARBITRARY, axis, radAngle); + quaternionRotator = new NonMutatingQuaternionRotator(Axis.LOCAL_ARBITRARY, jomlAxis, (float) radAngle); + } else { + vectorRotator = new NonMutatingVectorRotator(axis, radAngle); + quaternionRotator = new NonMutatingQuaternionRotator(axis, (float) radAngle); + } + + return toRotate.stream(event) + .map(object -> { + if (object instanceof Vector vectorToRotate) { + return vectorRotator.rotate(vectorToRotate); + } else if (object instanceof Quaternionf quaternion) { + return quaternionRotator.rotate(quaternion); + } + return null; + }) + .filter(Objects::nonNull) + .toArray(); + } + + @Override + public boolean isSingle() { + return toRotate.isSingle(); + } + + @Override + public Class getReturnType() { + return (matchedPattern == 1 || matchedPattern == 3) ? Quaternionf.class : toRotate.getReturnType(); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return switch (matchedPattern) { + case 0, 1 -> toRotate.toString(event, debug) + + " rotated around the " + axis + "-axis " + + "by " + angle.toString(event, debug) + " degrees"; + case 2 -> toRotate.toString(event, debug) + + " rotated around " + vector.toString(event, debug) + "-axis " + + "by " + angle.toString(event, debug) + " degrees"; + case 3 -> toRotate.toString(event, debug) + + " rotated by x " + x.toString(event, debug) + ", " + + "y " + y.toString(event, debug) + ", " + + "and z " + z.toString(event, debug); + default -> "invalid"; + }; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprTextOf.java b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprTextOf.java new file mode 100644 index 00000000000..1a502d098d5 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprTextOf.java @@ -0,0 +1,85 @@ +package org.skriptlang.skript.bukkit.misc.expressions; + +import org.bukkit.entity.TextDisplay; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +import ch.njol.skript.ServerPlatform; +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.skript.util.chat.BungeeConverter; +import ch.njol.skript.util.chat.ChatMessages; +import ch.njol.util.coll.CollectionUtils; +import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; +import net.md_5.bungee.api.chat.BaseComponent; + +@Name("Text Of") +@Description({ + "Returns or changes the text/string of displays.", + "Note that currently you can only use Skript chat codes when running Paper." +}) +@Examples("set text of the last spawned text display to \"example\"") +@Since("INSERT VERSION") +public class ExprTextOf extends SimplePropertyExpression { + + private static final boolean IS_RUNNING_PAPER = Skript.getServerPlatform() == ServerPlatform.BUKKIT_PAPER; + private static BungeeComponentSerializer serializer; + + static { + String types = ""; + if (Skript.classExists("org.bukkit.entity.Display")) { + serializer = BungeeComponentSerializer.get(); + types += "displays"; + } + // This is because this expression is setup to support future types. + // Remove this if non-versioning. + if (!types.isEmpty()) + register(ExprTextOf.class, String.class, "text[s]", types); + } + + @Override + public @Nullable String convert(Object object) { + if (object instanceof TextDisplay textDisplay) + return textDisplay.getText(); + return null; + } + + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case RESET -> CollectionUtils.array(); + case SET -> CollectionUtils.array(String.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + String value = delta == null ? null : (String) delta[0]; + for (Object object : getExpr().getArray(event)) { + if (!(object instanceof TextDisplay textDisplay)) + continue; + if (IS_RUNNING_PAPER && serializer != null && value != null) { + BaseComponent[] components = BungeeConverter.convert(ChatMessages.parseToArray(value)); + textDisplay.text(serializer.deserialize(components)); + } else { + textDisplay.setText(value); + } + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "text"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/DisplayRotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/DisplayRotator.java new file mode 100644 index 00000000000..423fff82510 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/DisplayRotator.java @@ -0,0 +1,42 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.bukkit.entity.Display; +import org.bukkit.util.Transformation; +import org.jetbrains.annotations.Contract; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +/** + * Rotates {@link Display}s around the X, Y, and Z axes, as well as any arbitrary axis. + * Supports all local axes. + * Modifies the left rotation of the display. + */ +public class DisplayRotator implements Rotator { + + private final QuaternionRotator qRotator; + + public DisplayRotator(Axis axis, float angle) { + qRotator = new QuaternionRotator(axis, angle); + } + + public DisplayRotator(Axis axis, Vector3f vector, float angle) { + qRotator = new QuaternionRotator(axis, vector, angle); + } + + @Override + @Contract("_ -> param1") + public Display rotate(Display input) { + Transformation transformation = input.getTransformation(); + Quaternionf leftRotation = transformation.getLeftRotation(); + input.setTransformation( + new Transformation( + transformation.getTranslation(), + qRotator.rotate(leftRotation), + transformation.getScale(), + transformation.getRightRotation() + ) + ); + return input; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingQuaternionRotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingQuaternionRotator.java new file mode 100644 index 00000000000..2d99ea9375f --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingQuaternionRotator.java @@ -0,0 +1,55 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.jetbrains.annotations.Contract; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.function.Function; + +/** + * Rotates {@link Quaternionf}s around the X, Y, and Z axes, as well as any arbitrary axis. + * Supports all local axes. + * Returns new quaternion objects rather than mutating the input quaternion. + */ +public class NonMutatingQuaternionRotator implements Rotator { + + private final Function rotator; + + /* + * NOTE: the apparent mismatch between the axis and methods for local/non-local is intentional. + * Rotating quaternions via rotateLocal results in a visual rotation around the global axis. + */ + + public NonMutatingQuaternionRotator(Axis axis, float angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateLocalX(angle, new Quaternionf()); + case Y -> (input) -> input.rotateLocalY(angle, new Quaternionf()); + case Z -> (input) -> input.rotateLocalZ(angle, new Quaternionf()); + case LOCAL_X -> (input) -> input.rotateX(angle, new Quaternionf()); + case LOCAL_Y -> (input) -> input.rotateY(angle, new Quaternionf()); + case LOCAL_Z -> (input) -> input.rotateZ(angle, new Quaternionf()); + case LOCAL_ARBITRARY -> throw new UnsupportedOperationException("Rotation around the " + axis + " axis requires additional data. Use a different constructor."); + case ARBITRARY -> (input) -> input; + }; + } + + public NonMutatingQuaternionRotator(Axis axis, Vector3f vector, float angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateLocalX(angle, new Quaternionf()); + case Y -> (input) -> input.rotateLocalY(angle, new Quaternionf()); + case Z -> (input) -> input.rotateLocalZ(angle, new Quaternionf()); + case LOCAL_X -> (input) -> input.rotateX(angle, new Quaternionf()); + case LOCAL_Y -> (input) -> input.rotateY(angle, new Quaternionf()); + case LOCAL_Z -> (input) -> input.rotateZ(angle, new Quaternionf()); + case LOCAL_ARBITRARY -> (input) -> input.rotateAxis(angle, vector, new Quaternionf()); + case ARBITRARY -> (input) -> input; + }; + } + + @Override + @Contract("_ -> new") + public Quaternionf rotate(Quaternionf input) { + return rotator.apply(input); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingVectorRotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingVectorRotator.java new file mode 100644 index 00000000000..e39c5d179e4 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/NonMutatingVectorRotator.java @@ -0,0 +1,43 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Contract; + +import java.util.function.Function; + +/** + * Rotates {@link Vector}s around the X, Y, and Z axes, as well as any arbitrary axis. + * Does not support local axes. + * Returns new vector objects rather than mutating the input vector. + */ +public class NonMutatingVectorRotator implements Rotator { + + private final Function rotator; + + public NonMutatingVectorRotator(Axis axis, double angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.clone().rotateAroundX(angle); + case Y -> (input) -> input.clone().rotateAroundY(angle); + case Z -> (input) -> input.clone().rotateAroundZ(angle); + case ARBITRARY -> throw new UnsupportedOperationException("Rotation around the " + axis + " axis requires additional data. Use a different constructor."); + case LOCAL_ARBITRARY, LOCAL_X, LOCAL_Y, LOCAL_Z -> (input) -> input; + }; + } + + public NonMutatingVectorRotator(Axis axis, Vector vector, double angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.clone().rotateAroundX(angle); + case Y -> (input) -> input.clone().rotateAroundY(angle); + case Z -> (input) -> input.clone().rotateAroundZ(angle); + case ARBITRARY -> (input) -> input.clone().rotateAroundNonUnitAxis(vector, angle); + case LOCAL_ARBITRARY, LOCAL_X, LOCAL_Y, LOCAL_Z -> (input) -> input; + }; + } + + @Override + @Contract("_ -> new") + public Vector rotate(Vector input) { + return rotator.apply(input); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/QuaternionRotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/QuaternionRotator.java new file mode 100644 index 00000000000..694510d6739 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/QuaternionRotator.java @@ -0,0 +1,54 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.jetbrains.annotations.Contract; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.function.Function; + +/** + * Rotates {@link Quaternionf}s around the X, Y, and Z axes, as well as any arbitrary axis. + * Supports all local axes. + */ +public class QuaternionRotator implements Rotator { + + private final Function rotator; + + /* + * NOTE: the apparent mismatch between the axis and methods for local/non-local is intentional. + * Rotating quaternions via rotateLocal results in a visual rotation around the global axis. + */ + + public QuaternionRotator(Axis axis, float angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateLocalX(angle); + case Y -> (input) -> input.rotateLocalY(angle); + case Z -> (input) -> input.rotateLocalZ(angle); + case LOCAL_X -> (input) -> input.rotateX(angle); + case LOCAL_Y -> (input) -> input.rotateY(angle); + case LOCAL_Z -> (input) -> input.rotateZ(angle); + case LOCAL_ARBITRARY -> throw new UnsupportedOperationException("Rotation around the " + axis + " axis requires additional data. Use a different constructor."); + case ARBITRARY -> (input) -> input; + }; + } + + public QuaternionRotator(Axis axis, Vector3f vector, float angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateLocalX(angle); + case Y -> (input) -> input.rotateLocalY(angle); + case Z -> (input) -> input.rotateLocalZ(angle); + case LOCAL_X -> (input) -> input.rotateX(angle); + case LOCAL_Y -> (input) -> input.rotateY(angle); + case LOCAL_Z -> (input) -> input.rotateZ(angle); + case LOCAL_ARBITRARY -> (input) -> input.rotateAxis(angle, vector); + case ARBITRARY -> (input) -> input; + }; + } + + @Override + @Contract("_ -> param1") + public Quaternionf rotate(Quaternionf input) { + return rotator.apply(input); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/Rotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/Rotator.java new file mode 100644 index 00000000000..01b64105227 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/Rotator.java @@ -0,0 +1,85 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import java.util.Locale; + +/** + * A functional interface to support uniform rotation semantics between various types. + * A rotator rotates objects around a specific {@link Axis} by a specific angle in radians. + * + * @param The class on which this rotator acts. + * + * @see Axis + */ +@FunctionalInterface +public interface Rotator { + + /** + * Rotates the input around the rotator's axis by the rotator's angle. + * May modify the input. + */ + T rotate(T input); + + /** + * Represents an axis around which to rotate. + */ + enum Axis { + /** + * The global X axis, relative to the world as a whole. + */ + X, + + /** + * The local X axis, relative to the object being rotated. + */ + LOCAL_X, + + /** + * The global Y axis, relative to the world as a whole. + */ + Y, + + /** + * The local Y axis, relative to the object being rotated. + */ + LOCAL_Y, + + /** + * The global Z axis, relative to the world as a whole. + */ + Z, + + /** + * The local Z axis, relative to the object being rotated. + */ + LOCAL_Z, + + /** + * An arbitrary global axis, relative to the world as a whole. + */ + ARBITRARY, + + /** + * An arbitrary local axis, relative to the object being rotated. + */ + LOCAL_ARBITRARY; + + @Override + public String toString() { + return super.toString().toLowerCase(Locale.ENGLISH).replace("_", " "); + } + + /** + * A helper method for converting from Bukkit {@link org.bukkit.Axis}. + * @param axis the axis to convert from + * @return the converted axis + */ + public static Axis fromBukkit(org.bukkit.Axis axis) { + return switch (axis) { + case X -> Axis.X; + case Y -> Axis.Y; + case Z -> Axis.Z; + }; + } + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/VectorRotator.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/VectorRotator.java new file mode 100644 index 00000000000..82760fe0fe2 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/VectorRotator.java @@ -0,0 +1,42 @@ +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Contract; + +import java.util.function.Function; + +/** + * Rotates {@link Vector}s around the X, Y, and Z axes, as well as any arbitrary axis. + * Does not support local axes. + */ +public class VectorRotator implements Rotator { + + private final Function rotator; + + public VectorRotator(Axis axis, double angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateAroundX(angle); + case Y -> (input) -> input.rotateAroundY(angle); + case Z -> (input) -> input.rotateAroundZ(angle); + case ARBITRARY -> throw new UnsupportedOperationException("Rotation around the " + axis + " axis requires additional data. Use a different constructor."); + case LOCAL_ARBITRARY, LOCAL_X, LOCAL_Y, LOCAL_Z -> (input) -> input; + }; + } + + public VectorRotator(Axis axis, Vector vector, double angle) { + this.rotator = switch (axis) { + case X -> (input) -> input.rotateAroundX(angle); + case Y -> (input) -> input.rotateAroundY(angle); + case Z -> (input) -> input.rotateAroundZ(angle); + case ARBITRARY -> (input) -> input.rotateAroundNonUnitAxis(vector, angle); + case LOCAL_ARBITRARY, LOCAL_X, LOCAL_Y, LOCAL_Z -> (input) -> input; + }; + } + + @Override + @Contract("_ -> param1") + public Vector rotate(Vector input) { + return rotator.apply(input); + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/package-info.java b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/package-info.java new file mode 100644 index 00000000000..55e4805502c --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/misc/rotation/package-info.java @@ -0,0 +1,5 @@ +@NonNullByDefault({DefaultLocation.PARAMETER, DefaultLocation.RETURN_TYPE}) +package org.skriptlang.skript.bukkit.misc.rotation; + +import org.eclipse.jdt.annotation.DefaultLocation; +import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index d95bf8b9907..2770d4d94fb 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1243,16 +1243,16 @@ entities: pattern: interaction(|1¦s) display: name: display¦s - pattern: display(|1¦s) + pattern: display(1:s| [entit(y|1:ies)]) block display: name: block display¦s - pattern: block display(|1¦s) + pattern: (block display(1:s| [entit(y|1:ies)]) [of %-blockdata/itemtype%]|alt:%-blockdata/itemtype% block display(1:s| [entit(y|1:ies)])) item display: name: item display¦s - pattern: item display(|1¦s) + pattern: (item display(1:s| [entit(y|1:ies)]) [of %-itemtype%]|alt:%-itemtype% item display(1:s| [entit(y|1:ies)])) text display: name: text display¦s - pattern: text display(|1¦s) + pattern: text display(1:s| [entit(y|1:ies)]) # 1.20.3 Entities breeze: name: breeze¦s @@ -2303,6 +2303,31 @@ transform reasons: unknown: unknown infection: infection, villager infection +# -- Display Billboards -- +billboards: + center: center, middle, center pivot + fixed: fixed + horizontal: horizontal, horizontal pivot + vertical: vertical, vertical pivot + +# -- Text Display Alignments -- +text alignments: + center: centered, center aligned, middle aligned + left: left aligned + right: right aligned + +# -- Item Display Transforms -- +item display transforms: + firstperson_lefthand: first person left handed, first person left hand, left handed in first person + firstperson_righthand: first person right handed, first person right hand, right handed in first person + fixed: fixed, fixed position + ground: ground, the ground + gui: gui, menu + head: head + none: no transform + thirdperson_lefthand: third person left handed, third person left hand, left handed in third person + thirdperson_righthand: third person right handed, third person right hand, right handed in third person + # -- Boolean -- boolean: true: @@ -2383,6 +2408,10 @@ types: quitreason: quit reason¦s @a inventoryclosereason: inventory close reason¦s @an transformreason: transform reason¦s @a + display: display¦s @a + billboard: billboard¦s @a + textalignment: text alignment¦s @a + itemdisplaytransform: item display transform¦s @an # Skript weathertype: weather type¦s @a @@ -2406,3 +2435,6 @@ types: # Hooks money: money region: region¦s + + # Other + quaternion: quaternion¦s @a diff --git a/src/test/skript/tests/misc/displaydata.sk b/src/test/skript/tests/misc/displaydata.sk new file mode 100644 index 00000000000..fccb62de0ab --- /dev/null +++ b/src/test/skript/tests/misc/displaydata.sk @@ -0,0 +1,68 @@ +test "display entity data" when running minecraft "1.19": + spawn a text display at spawn of world "world": + set {_text} to entity + + spawn a block display at spawn of world "world": + set {_block} to entity + + spawn an item display at spawn of world "world": + set {_item} to entity + + assert {_text} is a display with "text display was not a display" + assert {_text} is a text display with "text display was not a text display" + assert {_text} is not an item display with "text display was an item display" + assert {_text} is not a block display with "text display was a block display" + + assert {_block} is a display with "block display was not a display" + assert {_block} is a block display with "block display was not a block display" + assert {_block} is not an item display with "block display was an item display" + assert {_block} is not a text display with "block display was a text display" + + assert {_item} is a display with "item display was not a display" + assert {_item} is a item display with "item display was not an item display" + assert {_item} is not an text display with "item display was a text display" + assert {_item} is not a block display with "item display was a block display" + + spawn a block display of a spruce log at spawn of world "world": + set {_block1} to entity + + assert size of block displays is 2 with "did not find all block displays" + + spawn a red bed block display at spawn of world "world": + set {_block2} to entity + + assert size of block displays is 3 with "did not find all block displays" + assert size of block displays of a spruce log is 1 with "did not find correct amount of spruce block displays" + assert size of red bed block displays is 1 with "did not find correct amount of block display of a red beds" + + assert {_block2} is a display with "block display was not a display" + assert {_block2} is a block display with "block display was not a block display" + assert {_block2} is not a block display of a spruce log with "block display of a red bed was a block display of a spruce log" + assert {_block2} is a block display of a red bed with "block display of a red bed was not a block display of a red bed" + assert {_block2} is not an item display with "block display was an item display" + assert {_block2} is not a text display with "block display was a text display" + + spawn an item display of an oak log at spawn of world "world": + set {_item1} to entity + + spawn a item display of a diamond at spawn of world "world": + set {_item2} to entity + + assert size of item displays is 3 with "did not find all item displays" + assert size of item displays of oak log is 1 with "did not find correct amount of spruce item displays of oak logs" + assert size of item displays of diamond is 1 with "did not find correct amount of item displays of diamonds" + + assert {_item2} is a display with "item display was not a display" + assert {_item2} is a item display with "item display was not a item display" + assert {_item2} is not an item display of an oak log with "item display of a diamond was an oak log block display" + assert {_item2} is a diamond item display with "item display of a diamond was not a diamond block display" + assert {_item2} is not a block display with "item display was a block display" + assert {_item2} is not a text display with "item display was a text display" + + delete display within {_text} + delete display within {_item} + delete display within {_item1} + delete display within {_item2} + delete display within {_block} + delete display within {_block1} + delete display within {_block2} diff --git a/src/test/skript/tests/misc/displays.sk b/src/test/skript/tests/misc/displays.sk new file mode 100644 index 00000000000..e3d7df93a9e --- /dev/null +++ b/src/test/skript/tests/misc/displays.sk @@ -0,0 +1,17 @@ +test "spawn displays" when running minecraft "1.19.4": + spawn a text display at spawn of world "world": + set {_display} to event-display + assert billboard of display within {_display} is "fixed" parsed as billboard with "default billboard was not fixed" + set billboard of display within {_display} to "center" parsed as billboard + assert billboard of display within {_display} is center with "failed to set billboard to center" + clear all entities + spawn a block display at spawn of world "world": + set block data of event-display to grass_block[snowy=true] + set {_display} to event-display + assert block data of display within {_display} is grass_block[snowy=true] with "failed to compare block display block data" + spawn an item display of a diamond sword of sharpness at spawn of world "world": + assert item transform of event-display is no transform with "item transform was not default" + set item transform of event-display to head + assert item transform of event-display is head with "failed to set item transform to head" + assert item of event-display is a diamond sword of sharpness with "failed to compare against the item of the display %item of display%" + clear all entities diff --git a/src/test/skript/tests/syntaxes/effects/EffRotate.sk b/src/test/skript/tests/syntaxes/effects/EffRotate.sk new file mode 100644 index 00000000000..2de71407a8e --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffRotate.sk @@ -0,0 +1,74 @@ +test "rotate around global axis": + set {_v::1} to vector(1, 1, 1) + set {_v::2} to quaternion(1, 0, 0, 0) + + spawn block display at spawn of world "world": + set {_v::3} to entity + + rotate {_v::*} around y axis by 90 degrees + assert {_v::1} is vector(1, 1, -1) with "rotate vector around y axis failed" + assert {_v::2} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate quaternion around y axis failed" + assert left rotation of {_v::3} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate display around y axis failed" + + rotate {_v::*} around x axis by -90 degrees + assert {_v::1} is vector(1, -1, -1) with "rotate vector around x axis failed" + assert {_v::2} is quaternion(0.4999999701976776, -0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate quaternion around x axis failed" + assert left rotation of {_v::3} is quaternion(0.4999999701976776, -0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate display around x axis failed" + + rotate {_v::*} around z axis by -90 degrees + assert {_v::1} is vector(-1, -1, -1) with "rotate vector around z axis failed" + assert {_v::2} is quaternion(0, 0, 0.7071067094802856, -0.7071067094802856) with "rotate quaternion around z axis failed" + assert left rotation of {_v::3} is quaternion(0, 0, 0.7071067094802856, -0.7071067094802856) with "rotate display around z axis failed" + + delete entity within {_v::3} + +test "rotate around local axis": + set {_v::1} to vector(1, 1, 1) + set {_v::2} to quaternion(1, 0, 0, 0) + spawn block display at spawn of world "world": + set {_v::3} to entity + + rotate {_v::*} around local y axis by 90 degrees + assert {_v::1} is vector(1,1,1) with "rotate vector around local y axis unexpectedly modified vector" + assert {_v::2} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate quaternion around local y axis failed" + assert left rotation of {_v::3} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate display around local y axis failed" + + rotate {_v::*} around local x axis by 90 degrees + assert {_v::1} is vector(1,1,1) with "rotate vector around local x axis unexpectedly modified vector" + assert {_v::2} is quaternion(0.4999999701976776, 0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate quaternion around local x axis failed" + assert left rotation of {_v::3} is quaternion(0.4999999701976776, 0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate display around local x axis failed" + + rotate {_v::*} around local z axis by 90 degrees + assert {_v::1} is vector(1,1,1) with "rotate vector around local z axis unexpectedly modified vector" + assert {_v::2} is quaternion(0.7071067094802856, 0.7071067094802856, 0, 0) with "rotate quaternion around local z axis failed" + assert left rotation of {_v::3} is quaternion(0.7071067094802856, 0.7071067094802856, 0, 0) with "rotate display around local z axis failed" + + delete entity within {_v::3} + +test "vector rotate around vector": + set {_x} to vector(1, 0, 0) + set {_y} to vector(0, 1, 0) + set {_z} to vector(0, 0, 1) + + set {_v} to {_x} + rotate {_v} around {_y} by -90 degrees + assert {_v} is {_z} with "rotate around y vector failed" + + rotate {_v} around {_x} by -90 degrees + assert {_v} is {_y} with "rotate around x vector failed" + + rotate {_v} around {_z} by -90 degrees + assert {_v} is {_x} with "rotate around z vector failed" + + rotate {_v} around vector(1, 1, 1) by 180 degrees + assert {_v} is vector(-1/3, 2/3, 2/3) with "rotate around non-orthogonal vector failed" + + set {_v} to vector(2, 0, 0) + rotate {_v} around vector(0, 10, 0) by 90 degrees + assert {_v} is vector(0, 0, -2) with "rotate around non-unit vector failed" + + +test "quaternion rotate by ZYX rotation": + set {_q} to quaternion(1,0,0,0) + rotate {_q} by x 90, y 90, z 90 + assert {_q} is quaternion(0.7071067094802856, 0, 0.7071067094802856, 0) with "failed to rotate quaternion by ZYX" diff --git a/src/test/skript/tests/syntaxes/effects/EffTextDisplayCanSeeThtroughBlocks.sk b/src/test/skript/tests/syntaxes/effects/EffTextDisplayCanSeeThtroughBlocks.sk new file mode 100644 index 00000000000..ffcb160f5ad --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffTextDisplayCanSeeThtroughBlocks.sk @@ -0,0 +1,19 @@ +test "text display see through blocks": + spawn item display at spawn of world "world": + set {_id} to entity + + assert {_id} isn't visible through walls with "item display can be seen through walls" + force {_id} to be visible through walls + assert {_id} isn't visible through walls with "item display can be seen through walls" + + delete entity within {_id} + spawn text display at spawn of world "world": + set {_td} to entity + + assert {_td} isn't visible through walls with "text display can be seen through walls by default" + force {_td} to be visible through walls + assert {_td} is visible through walls with "failed to make see through" + prevent {_td} from being visible through walls + assert {_td} isn't visible through walls with "failed to prevent see through" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/effects/EffTextDisplayDropShadow.sk b/src/test/skript/tests/syntaxes/effects/EffTextDisplayDropShadow.sk new file mode 100644 index 00000000000..9516a0a7106 --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffTextDisplayDropShadow.sk @@ -0,0 +1,19 @@ +test "text display drop shadow": + spawn an item display at spawn of world "world": + set {_id} to entity + + assert {_id} doesn't have drop shadow with "item display has drop shadow" + add drop shadow to {_id} + assert {_id} doesn't have drop shadow with "item display has drop shadow" + + delete entity within {_id} + spawn a text display at spawn of world "world": + set {_td} to entity + + assert {_td} doesn't have drop shadow with "text display has drop shadow by default" + add drop shadow to {_td} + assert {_td}'s text has drop shadow with "failed to set drop shadow" + remove drop shadow from text of {_td} + assert {_td}'s text doesn't have drop shadow with "failed to remove drop shadow" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/effects/EffVectorRotateAroundAnother.sk b/src/test/skript/tests/syntaxes/effects/EffVectorRotateAroundAnother.sk deleted file mode 100644 index 07d21ec5730..00000000000 --- a/src/test/skript/tests/syntaxes/effects/EffVectorRotateAroundAnother.sk +++ /dev/null @@ -1,21 +0,0 @@ -test "vector rotate around vector": - set {_x} to vector(1, 0, 0) - set {_y} to vector(0, 1, 0) - set {_z} to vector(0, 0, 1) - - set {_v} to {_x} - rotate {_v} around {_y} by -90 degrees - assert {_v} is {_z} with "rotate around y vector failed (expected %{_z}%, got %{_v}%)" - - rotate {_v} around {_x} by -90 degrees - assert {_v} is {_y} with "rotate around x vector failed (expected %{_y}%, got %{_v}%)" - - rotate {_v} around {_z} by -90 degrees - assert {_v} is {_x} with "rotate around z vector failed (expected %{_x}%, got %{_v}%)" - - rotate {_v} around vector(1, 1, 1) by 180 degrees - assert {_v} is vector(-1/3, 2/3, 2/3) with "rotate around non-orthogonal vector failed (expected %vector(-1/3, 2/3, 2/3)%, got %{_v}%)" - - set {_v} to vector(2, 0, 0) - rotate {_v} around vector(0, 10, 0) by 90 degrees - assert {_v} is vector(0, 0, -2) with "rotate around non-unit vector failed (expected %vector(0, 0, -2)%, got %{_v}%)" diff --git a/src/test/skript/tests/syntaxes/effects/EffVectorRotateXYZ.sk b/src/test/skript/tests/syntaxes/effects/EffVectorRotateXYZ.sk deleted file mode 100644 index 72adc840f8f..00000000000 --- a/src/test/skript/tests/syntaxes/effects/EffVectorRotateXYZ.sk +++ /dev/null @@ -1,11 +0,0 @@ -test "vector rotate around axis": - - set {_v} to vector(1, 1, 1) - rotate {_v} around y axis by 90 degrees - assert {_v} is vector(1, 1, -1) with "rotate around y vector failed (expected %vector(1, 1, -1)%, got %{_v}%)" - - rotate {_v} around x axis by -90 degrees - assert {_v} is vector(1, -1, -1) with "rotate around x vector failed (expected %vector(1, -1, -1)%, got %{_v}%)" - - rotate {_v} around z axis by -90 degrees - assert {_v} is vector(-1, -1, -1) with "rotate around z vector failed (expected %vector(-1, -1, -1)%, got %{_v}%)" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprColorOf.sk b/src/test/skript/tests/syntaxes/expressions/ExprColorOf.sk new file mode 100644 index 00000000000..ed68ede4f20 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprColorOf.sk @@ -0,0 +1,30 @@ +# TODO: write tests for non-display colors + +# test "color of blocks": + +# test "color of items": + +# test "color of entities": + +# test "color of fireworks": + +test "color of displays" when running minecraft "1.19.4": + spawn a text display at spawn of world "world": + set {_e} to entity + + assert color of {_e} is rgb(0,0,0, 64) with "default background colour failed" + + set colour of {_e} to red + assert color of {_e} is red with "failed to set background colour" + + set colour of {_e} to rgb(1, 50, 200) + assert color of {_e} is rgb(1, 50, 200) with "failed to set background colour to rgb" + + reset colour of {_e} + assert color of {_e} is rgb(0, 0, 0, 64) with "delete background colour failed" + + set color of all text displays to rgb(0, 10, 0) + assert color of all text displays is rgb(0, 10, 0) with "set all background colour failed" + + delete entity within {_e} + diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayBillboard.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayBillboard.sk new file mode 100644 index 00000000000..81550626e4b --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayBillboard.sk @@ -0,0 +1,50 @@ +test "display billboard" when running minecraft "1.19.4": + + spawn a text display at spawn of world "world": + set {_e} to entity + + assert bill-boarding of {_e} is fixed with "incorrect default bill-boarding" + + set bill-boarding of {_e} to vertical + assert bill-boarding of {_e} is vertical with "failed to set bill-boarding" + set bill-boarding of {_e} to horizontal + assert bill-boarding of {_e} is horizontal with "failed to set bill-boarding" + set bill-boarding of {_e} to center + assert bill-boarding of {_e} is center with "failed to set bill-boarding" + reset bill-boarding of {_e} + assert bill-boarding of {_e} is fixed with "failed to reset bill-boarding" + + delete entity within {_e} + + spawn an item display at spawn of world "world": + set {_e} to entity + + assert bill-boarding of {_e} is fixed with "incorrect default bill-boarding" + + set bill-boarding of {_e} to vertical + assert bill-boarding of {_e} is vertical with "failed to set bill-boarding" + set bill-boarding of {_e} to horizontal + assert bill-boarding of {_e} is horizontal with "failed to set bill-boarding" + set bill-boarding of {_e} to center + assert bill-boarding of {_e} is center with "failed to set bill-boarding" + reset bill-boarding of {_e} + assert bill-boarding of {_e} is fixed with "failed to reset bill-boarding" + + delete entity within {_e} + + + spawn a block display at spawn of world "world": + set {_e} to entity + + assert bill-boarding of {_e} is fixed with "incorrect default bill-boarding" + + set bill-boarding of {_e} to vertical + assert bill-boarding of {_e} is vertical with "failed to set bill-boarding" + set bill-boarding of {_e} to horizontal + assert bill-boarding of {_e} is horizontal with "failed to set bill-boarding" + set bill-boarding of {_e} to center + assert bill-boarding of {_e} is center with "failed to set bill-boarding" + reset bill-boarding of {_e} + assert bill-boarding of {_e} is fixed with "failed to reset bill-boarding" + + delete entity within {_e} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayBrightness.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayBrightness.sk new file mode 100644 index 00000000000..97233552d85 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayBrightness.sk @@ -0,0 +1,88 @@ +test "display brightness" when running minecraft "1.19.4": + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert brightness override of {_e::*} is not set with "default displays have unexpected brightness override" + assert block light level override of {_e::*} is not set with "default displays have unexpected sky light override" + assert sky light override of {_e::*} is not set with "default displays have unexpected block light override" + + # brightness overrides + set brightness override of {_e::*} to 2 + assert brightness override of {_e::*} is 2, 2, 2, 2, 2, and 2 with "failed setting brightness" + assert block light level override of {_e::*} is 2 with "failed setting brightness" + assert sky light override of {_e::*} is 2 with "failed setting brightness" + + set brightness override of {_e::*} to -1 + assert brightness override of {_e::*} is 0, 0, 0, 0, 0, and 0 with "failed setting brightness" + assert block light level override of {_e::*} is 0 with "failed setting brightness" + assert sky light override of {_e::*} is 0 with "failed setting brightness" + + reset brightness override of {_e::*} + assert brightness override of {_e::*} is not set with "failed resetting brightness" + assert block light level override of {_e::*} is not set with "failed resetting brightness" + assert sky light override of {_e::*} is not set with "failed resetting brightness" + + # sky light + + set sky light override of {_e::*} to 5 + assert brightness override of {_e::*} is 5 with "failed setting sky light" + assert block light level override of {_e::*} is 5 with "failed setting initial block light" + assert sky light override of {_e::*} is 5 with "failed setting sky light" + + set sky light override of {_e::*} to -1 + assert brightness override of {_e::*} is 5, 0, 5, 0, 5, and 0 with "failed setting sky light" + assert block light level override of {_e::*} is 5 with "failed setting sky light" + assert sky light override of {_e::*} is 0 with "failed setting sky light" + + add 5 to sky light override of {_e::*} + assert brightness override of {_e::*} is 5 with "failed adding sky light" + assert block light level override of {_e::*} is 5 with "failed adding sky light" + assert sky light override of {_e::*} is 5 with "failed adding sky light" + + set sky light override of {_e::*} to {_} + assert brightness override of {_e::*} is 5 with "failed setting sky light" + assert block light level override of {_e::*} is 5 with "failed setting sky light" + assert sky light override of {_e::*} is 5 with "failed setting sky light" + + remove 10 from sky light override of {_e::*} + assert brightness override of {_e::*} is 5, 0, 5, 0, 5, and 0 with "failed adding sky light" + assert block light level override of {_e::*} is 5 with "failed adding sky light" + assert sky light override of {_e::*} is 0 with "failed adding sky light" + + clear brightness override of {_e::*} + assert brightness override of {_e::*} is not set with "failed deleting brightness" + assert block light level override of {_e::*} is not set with "failed deleting brightness" + assert sky light override of {_e::*} is not set with "failed deleting brightness" + + # block light + + set block light override of {_e::*} to 5 + assert brightness override of {_e::*} is 5 with "failed setting block light" + assert block light level override of {_e::*} is 5 with "failed setting initial block light" + assert sky light override of {_e::*} is 5 with "failed setting block light" + + set block light override of {_e::*} to infinity value + assert brightness override of {_e::*} is 15, 5, 15, 5, 15, and 5 with "failed setting block light" + assert block light level override of {_e::*} is 15 with "failed setting block light" + assert sky light override of {_e::*} is 5 with "failed setting block light" + + add 5 to block light override of {_e::*} + assert brightness override of {_e::*} is 15, 5, 15, 5, 15, and 5 with "failed adding block light" + assert block light level override of {_e::*} is 15 with "failed adding block light" + assert sky light override of {_e::*} is 5 with "failed adding block light" + + set block light override of {_e::*} to {_} + assert brightness override of {_e::*} is 15, 5, 15, 5, 15, and 5 with "failed setting block light" + assert block light level override of {_e::*} is 15 with "failed setting block light" + assert sky light override of {_e::*} is 5 with "failed setting block light" + + remove 10 from block light override of {_e::*} + assert brightness override of {_e::*} is 5 with "failed adding block light" + assert block light level override of {_e::*} is 5 with "failed adding block light" + assert sky light override of {_e::*} is 5 with "failed adding block light" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayGlowOverride.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayGlowOverride.sk new file mode 100644 index 00000000000..2442d6ef49a --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayGlowOverride.sk @@ -0,0 +1,21 @@ +test "display glow color override" when running minecraft "1.19": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert glow colour override of {_e::*} is not set with "default override was set" + + set glow colour override of {_e::*} to blue + assert glow colour override of {_e::*} is blue with "failed to set to colour" + + set glow colour override of {_e::*} to rgb(1, 2, 3) + assert glow colour override of {_e::*} is rgb(1, 2, 3) with "failed to set to rgb colour" + + reset glow colour override of {_e::*} + assert glow colour override of {_e::*} is not set with "failed to reset override" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayHeightWidth.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayHeightWidth.sk new file mode 100644 index 00000000000..3b0e74e2d23 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayHeightWidth.sk @@ -0,0 +1,39 @@ +test "display height/width" when running minecraft "1.19": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert display width of {_e::*} is 0 with "default width was not 0" + assert display height of {_e::*} is 0 with "default height was not 0" + + set display height of {_e::*} to 5 + assert display height of {_e::*} is 5 with "failed to set to height" + assert display width of {_e::*} is 0 with "setting height modified width" + + set display width of {_e::*} to 0.1 + assert display height of {_e::*} is 5 with "setting width modified height" + assert display width of {_e::*} is 0.1 with "failed to set to width" + + set display height of {_e::*} to infinity value + assert display height of {_e::*} is 5 with "set to height to infinity" + + set display height of {_e::*} to -10 + assert display height of {_e::*} is 0 with "set to height to negative value" + + add 20 to display height of {_e::*} + assert display height of {_e::*} is 20 with "add 20 to height" + + subtract 25 from display height of {_e::*} + assert display height of {_e::*} is 0 with "subtract 25 from height" + + subtract -2.5 from display height of {_e::*} + assert display height of {_e::*} is 2.5 with "subtract -2.5 from height" + + reset display height of {_e::*} + assert display height of {_e::*} is 0 with "reset height" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayInterpolation.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayInterpolation.sk new file mode 100644 index 00000000000..4f5061baf68 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayInterpolation.sk @@ -0,0 +1,33 @@ +test "display interpolation" when running minecraft "1.19.4": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert interpolation delay of {_e::*} is 0 ticks with "default delay was not 0" + assert interpolation duration of {_e::*} is 0 seconds with "default duration was not 0" + + set interpolation delay of {_e::*} to 5 ticks + assert interpolation delay of {_e::*} is 5 ticks with "failed to set to delay" + assert interpolation duration of {_e::*} is 0 seconds with "setting height modified duration" + + set interpolation duration of {_e::*} to 0.1 seconds + assert interpolation delay of {_e::*} is 5 ticks with "setting width modified delay" + assert interpolation duration of {_e::*} is 0.1 seconds with "failed to set to duration" + + set interpolation delay of {_e::*} to 0 ticks + assert interpolation delay of {_e::*} is 0 ticks with "set to delay to 0 value" + + add 20 ticks to interpolation delay of {_e::*} + assert interpolation delay of {_e::*} is 20 ticks with "add 20 to delay" + + subtract 25 ticks from interpolation delay of {_e::*} + assert interpolation delay of {_e::*} is 0 ticks with "subtract 25 from delay" + + reset interpolation delay of {_e::*} + assert interpolation delay of {_e::*} is 0 seconds with "reset delay" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayShadow.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayShadow.sk new file mode 100644 index 00000000000..ad2d9ef9088 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayShadow.sk @@ -0,0 +1,39 @@ +test "display radius/strength" when running minecraft "1.19": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert shadow strength of {_e::*} is 1 with "default strength was not 0" + assert shadow radius of {_e::*} is 0 with "default radius was not 0" + + set shadow radius of {_e::*} to 5 + assert shadow radius of {_e::*} is 5 with "failed to set to radius" + assert shadow strength of {_e::*} is 1 with "setting radius modified strength" + + set shadow strength of {_e::*} to 0.1 + assert shadow radius of {_e::*} is 5 with "setting strength modified radius" + assert shadow strength of {_e::*} is 0.1 with "failed to set to strength" + + set shadow radius of {_e::*} to infinity value + assert shadow radius of {_e::*} is 5 with "set to radius to infinity" + + set shadow radius of {_e::*} to -10 + assert shadow radius of {_e::*} is 0 with "set to radius to negative value" + + add 20 to shadow radius of {_e::*} + assert shadow radius of {_e::*} is 20 with "add 20 to radius" + + subtract 25 from shadow radius of {_e::*} + assert shadow radius of {_e::*} is 0 with "subtract 25 from radius" + + subtract -2.5 from shadow radius of {_e::*} + assert shadow radius of {_e::*} is 2.5 with "subtract -2.5 from radius" + + reset shadow radius of {_e::*} + assert shadow radius of {_e::*} is 0 with "reset radius" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayTeleportationDuration.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayTeleportationDuration.sk new file mode 100644 index 00000000000..9f57c210169 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayTeleportationDuration.sk @@ -0,0 +1,30 @@ +test "display teleportation duration" when running minecraft "1.20.4": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert teleportation duration of {_e::*} is 0 ticks with "default duration was not 0" + + set teleportation duration of {_e::*} to 5 ticks + assert teleportation duration of {_e::*} is 5 ticks with "failed to set to duration" + + set interpolation duration of {_e::*} to 0.1 seconds + assert teleportation duration of {_e::*} is 5 ticks with "setting width modified duration" + + set teleportation duration of {_e::*} to 0 ticks + assert teleportation duration of {_e::*} is 0 ticks with "set to duration to 0 value" + + add 20 ticks to teleportation duration of {_e::*} + assert teleportation duration of {_e::*} is 20 ticks with "add 20 to duration" + + subtract 25 ticks from teleportation duration of {_e::*} + assert teleportation duration of {_e::*} is 0 ticks with "subtract 25 from duration" + + reset teleportation duration of {_e::*} + assert teleportation duration of {_e::*} is 0 seconds with "reset duration" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayTransformation.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayTransformation.sk new file mode 100644 index 00000000000..4eaac92da0b --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayTransformation.sk @@ -0,0 +1,59 @@ +test "display transformation rotations" when running minecraft "1.19.4": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert left rotation of {_e::*} is quaternion(1, 0, 0, 0) with "default left rotation was wrong" + assert right rotation of {_e::*} is quaternion(1, 0, 0, 0) with "default right rotation was wrong" + + set left rotation of {_e::*} to quaternion(1, 2, 3, 4) + assert left rotation of {_e::*} is quaternion(1, 2, 3, 4) with "failed to set left rotation" + assert right rotation of {_e::*} is quaternion(1, 0, 0, 0) with "setting left affected right rotation" + + set left rotation of {_e::*} to quaternion(1, infinity value, 3, 4) + assert left rotation of {_e::*} is quaternion(1, 2, 3, 4) with "set left rotation to invalid value" + + set right rotation of {_e::*} to axisAngle(45, vector(0, 0, 1)) + assert left rotation of {_e::*} is quaternion(1, 2, 3, 4) with "setting right affected left rotation" + assert right rotation of {_e::*} is quaternion(0.923879504, 0, 0, 0.382683455) with "failed to set right to an axisangle" + + reset left rotation of {_e::*} + assert left rotation of {_e::*} is quaternion(1, 0, 0, 0) with "failed to reset left" + + delete entities within {_e::*} + + +test "display transformation translation / scales" when running minecraft "1.19.4": + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert display scale of {_e::*} is vector(1,1,1) with "default display scale was wrong" + assert display translation of {_e::*} is vector(0,0,0) with "default display translation was wrong" + + set display scale of {_e::*} to vector(1,2,1) + assert display scale of {_e::*} is vector(1,2,1) with "failed to set display scale" + assert display translation of {_e::*} is vector(0,0,0) with "setting display scale affected display translation" + + set display translation of {_e::*} to vector(1,-2,1) + assert display scale of {_e::*} is vector(1,2,1) with "setting display translation affected display scale" + assert display translation of {_e::*} is vector(1,-2,1) with "failed to set display translation" + + set display scale of {_e::*} to vector(NaN value, 0, 0) + assert display scale of {_e::*} is vector(1,2,1) with "set display scale to invalid value" + + reset display scale of {_e::*} + assert display scale of {_e::*} is vector(1,1,1) with "failed to reset display scale" + + reset display translation of {_e::*} + assert display translation of {_e::*} is vector(0,0,0) with "failed to reset display scale" + + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprDisplayViewRange.sk b/src/test/skript/tests/syntaxes/expressions/ExprDisplayViewRange.sk new file mode 100644 index 00000000000..5428dd8b8ba --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprDisplayViewRange.sk @@ -0,0 +1,27 @@ +test "display view range" when running minecraft "1.19": + + spawn block display at spawn of world "world": + set {_e::1} to entity + spawn item display at spawn of world "world": + set {_e::2} to entity + spawn text display at spawn of world "world": + set {_e::3} to entity + + assert view range of {_e::*} is 1 with "default view range is incorrect" + + set view range of {_e::*} to 0 + assert view range of {_e::*} is 0 with "failed to set view range" + + add 20.1 to view range of {_e::*} + assert view range of {_e::*} is 20.1 with "failed to add to view range" + + remove 50.75 from view range of {_e::*} + assert view range of {_e::*} is 0 with "failed to remove from view range" + + reset view range of {_e::*} + assert view range of {_e::*} is 1 with "failed to reset view range" + + set view range of {_e::*} to NaN value + assert view range of {_e::*} is 1 with "set view range to NaN value changed value" + + delete entities within {_e::*} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprItemDisplayTransform.sk b/src/test/skript/tests/syntaxes/expressions/ExprItemDisplayTransform.sk new file mode 100644 index 00000000000..70e676e7b0c --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprItemDisplayTransform.sk @@ -0,0 +1,40 @@ +test "item display transforms" when running minecraft "1.19.4": + + spawn block display at spawn of world "world": + set {_bd} to entity + + assert item transform of {_bd} is not set with "block display has item transform" + set item transform of {_bd} to fixed + assert item transform of {_bd} is not set with "block display has item transform" + reset item transform of {_bd} + assert item transform of {_bd} is not set with "block display has item transform" + + delete entity within {_bd} + spawn item display of a diamond sword at spawn of world "world": + set {_id} to entity + + assert item transform of {_id} is no transform with "item display has wrong default transform" + + set item transform of {_id} to third person left hand + assert item transform of {_id} is third person left hand with "failed to set item transform" + set item transform of {_id} to third person right hand + assert item transform of {_id} is third person right hand with "failed to set item transform" + set item transform of {_id} to first person left hand + assert item transform of {_id} is first person left hand with "failed to set item transform" + set item transform of {_id} to first person right hand + assert item transform of {_id} is first person right hand with "failed to set item transform" + set item transform of {_id} to no transform + assert item transform of {_id} is no transform with "failed to set item transform" + set item transform of {_id} to fixed + assert item transform of {_id} is fixed with "failed to set item transform" + set item transform of {_id} to head + assert item transform of {_id} is head with "failed to set item transform" + set item transform of {_id} to gui + assert item transform of {_id} is gui with "failed to set item transform" + set item transform of {_id} to ground + assert item transform of {_id} is ground with "failed to set item transform" + + reset item transform of {_id} + assert item transform of {_id} is no transform with "failed to reset item transform" + + delete entity within {_id} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprQuaternionAxisAngle.sk b/src/test/skript/tests/syntaxes/expressions/ExprQuaternionAxisAngle.sk new file mode 100644 index 00000000000..1f4d69fe321 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprQuaternionAxisAngle.sk @@ -0,0 +1,19 @@ +test "quaternion axis angle" when running minecraft "1.19": + set {_v} to vector(1, 2, 3) + set {_q} to axisAngle(45, {_v}) + assert rotation angle of {_q} is 45 with "angle of quaternion failed" + assert rotation axis of {_q} is {_v} with "axis of quaternion failed" + + set rotation angle of {_q} to 135 + assert rotation angle of {_q} is 135 with "changing angle of quaternion failed" + assert rotation axis of {_q} is {_v} with "changing angle of quaternion modified axis" + + set rotation axis of {_q} to vector(0, 10, 0) + assert rotation angle of {_q} is 135 with "changing axis of quaternion failed modified angle" + assert rotation axis of {_q} is vector(0, 10, 0) with "changing axis of quaternion failed" + + + set {_q} to quaternion(1, 0, 0, 0) + + assert rotation angle of {_q} is 0 with "angle of identity quaternion failed" + assert rotation axis of {_q} is vector(0,0,1) with "axis of identity quaternion failed" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprRotate.sk b/src/test/skript/tests/syntaxes/expressions/ExprRotate.sk new file mode 100644 index 00000000000..14271ae9a8c --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprRotate.sk @@ -0,0 +1,51 @@ +test "rotate around global axis": + set {_v::1} to quaternion(1, 0, 0, 0) + + set {_v::*} to {_v::*} rotated around y axis by 90 degrees + assert {_v::1} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate quaternion around y axis failed" + + set {_v::*} to {_v::*} rotated around x axis by -90 degrees + assert {_v::1} is quaternion(0.4999999701976776, -0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate quaternion around x axis failed" + + set {_v::*} to {_v::*} rotated around z axis by -90 degrees + assert {_v::1} is quaternion(0, 0, 0.7071067094802856, -0.7071067094802856) with "rotate quaternion around z axis failed" + +test "rotate around local axis": + set {_v::1} to quaternion(1, 0, 0, 0) + + set {_v::*} to {_v::*} rotated around local y axis by 90 degrees + assert {_v::1} is quaternion(0.7071067690849304, 0, 0.7071067690849304, 0) with "rotate quaternion around local y axis failed" + + set {_v::*} to {_v::*} rotated around local x axis by 90 degrees + assert {_v::1} is quaternion(0.4999999701976776, 0.4999999701976776, 0.4999999701976776, -0.4999999701976776) with "rotate quaternion around local x axis failed" + + set {_v::*} to {_v::*} rotated around local z axis by 90 degrees + assert {_v::1} is quaternion(0.7071067094802856, 0.7071067094802856, 0, 0) with "rotate quaternion around local z axis failed" + +test "vector rotate around vector": + set {_x} to vector(1, 0, 0) + set {_y} to vector(0, 1, 0) + set {_z} to vector(0, 0, 1) + + set {_v} to {_x} + set {_v} to {_v} rotated around {_y} by -90 degrees + assert {_v} is {_z} with "rotate around y vector failed" + + set {_v} to {_v} rotated around {_x} by -90 degrees + assert {_v} is {_y} with "rotate around x vector failed" + + set {_v} to {_v} rotated around {_z} by -90 degrees + assert {_v} is {_x} with "rotate around z vector failed" + + set {_v} to {_v} rotated around vector(1, 1, 1) by 180 degrees + assert {_v} is vector(-1/3, 2/3, 2/3) with "rotate around non-orthogonal vector failed" + + set {_v} to vector(2, 0, 0) + set {_v} to {_v} rotated around vector(0, 10, 0) by 90 degrees + assert {_v} is vector(0, 0, -2) with "rotate around non-unit vector failed" + + +test "quaternion rotate by ZYX rotation": + set {_q} to quaternion(1,0,0,0) + set {_q} to {_q} rotated by x 90, y 90, z 90 + assert {_q} is quaternion(0.7071067094802856, 0, 0.7071067094802856, 0) with "failed to rotate quaternion by ZYX" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayAlignment.sk b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayAlignment.sk new file mode 100644 index 00000000000..e62d9f7172c --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayAlignment.sk @@ -0,0 +1,24 @@ +test "text alignment" when running minecraft "1.19.4": + + spawn item display at spawn of world "world": + set {_id} to entity + + assert text alignment of {_id} is not set with "item display has text alignment" + set text alignment of {_id} to centered + assert text alignment of {_id} is not set with "item display has text alignment" + reset text alignment of {_id} + assert text alignment of {_id} is not set with "item display has text alignment" + + delete entity within {_id} + spawn text display at spawn of world "world": + set {_td} to entity + + assert text alignment of {_td} is centered with "failed default text alignment" + set text alignment of {_td} to left aligned + assert text alignment of {_td} is left aligned with "failed to set text alignment" + set text alignment of {_td} to right aligned + assert text alignment of {_td} is right aligned with "failed to set text alignment" + reset text alignment of {_td} + assert text alignment of {_td} is centered with "failed to set text alignment" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayLineWidth.sk b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayLineWidth.sk new file mode 100644 index 00000000000..9301419b8cd --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayLineWidth.sk @@ -0,0 +1,33 @@ +test "line width" when running minecraft "1.19.4": + + spawn item display at spawn of world "world": + set {_id} to entity + + assert line width of {_id} is not set with "item display has line width" + set line width of {_id} to 55 + assert line width of {_id} is not set with "item display has line width" + reset line width of {_id} + assert line width of {_id} is not set with "item display has line width" + + delete entity within {_id} + spawn text display at spawn of world "world": + set {_td} to entity + + assert line width of {_td} is 200 with "failed default line width" + set line width of {_td} to 185.4 + assert line width of {_td} is 185 with "failed to set line width" + set line width of {_td} to -50 + assert line width of {_td} is 0 with "failed to set line width" + set line width of {_td} to 900 + assert line width of {_td} is 900 with "failed to set line width" + add 50 to line width of {_td} + assert line width of {_td} is 950 with "failed to add to line width" + remove 1000 from line width of {_td} + assert line width of {_td} is 0 with "failed to remove from line width" + reset line width of {_td} + assert line width of {_td} is 200 with "failed to reset line width" + + set line width of {_td} to infinity value + assert line width of {_td} is 2147483647 with "failed to set line width" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayOpacity.sk b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayOpacity.sk new file mode 100644 index 00000000000..521c659f931 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprTextDisplayOpacity.sk @@ -0,0 +1,33 @@ +test "text opacity" when running minecraft "1.19.4": + + spawn item display at spawn of world "world": + set {_id} to entity + + assert opacity of {_id} is not set with "item display has opacity" + set opacity of {_id} to 55 + assert opacity of {_id} is not set with "item display has opacity" + reset opacity of {_id} + assert opacity of {_id} is not set with "item display has opacity" + + delete entity within {_id} + spawn text display at spawn of world "world": + set {_td} to entity + + assert opacity of {_td} is -1 with "failed default opacity" + set opacity of {_td} to 185.4 + assert opacity of {_td} is 127 with "failed to set opacity" + set opacity of {_td} to -50 + assert opacity of {_td} is -50 with "failed to set opacity" + set opacity of {_td} to -900 + assert opacity of {_td} is -127 with "failed to set opacity" + add 50 to opacity of {_td} + assert opacity of {_td} is -77 with "failed to add to opacity" + remove 1000 from opacity of {_td} + assert opacity of {_td} is -127 with "failed to remove from opacity" + reset opacity of {_td} + assert opacity of {_td} is -1 with "failed to reset opacity" + + set opacity of {_td} to infinity value + assert opacity of {_td} is 127 with "failed to set opacity" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprTextOf.sk b/src/test/skript/tests/syntaxes/expressions/ExprTextOf.sk new file mode 100644 index 00000000000..706df1fa73f --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprTextOf.sk @@ -0,0 +1,25 @@ +test "text of text displays": + spawn item display at spawn of world "world": + set {_id} to entity + + assert text of {_id} is not set with "item display has text" + set text of {_id} to "55" + assert text of {_id} is not set with "item display has text" + reset text of {_id} + assert text of {_id} is not set with "item display has text" + + delete entity within {_id} + spawn text display at spawn of world "world": + set {_td} to entity + + assert text of {_td} is "" with "failed default text" + set text of {_td} to "hello!" + assert text of {_td} is "hello!" with "failed to set text" + set text of {_td} to "" + assert text of {_td} is "" with "failed to set text" + set text of {_td} to "èa_.21&*5ö!" + assert text of {_td} is "èa_.21&*5ö!" with "failed to set text" + reset text of {_td} + assert text of {_td} is "" with "failed to reset text" + + delete entity within {_td} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprVectorXYZ.sk b/src/test/skript/tests/syntaxes/expressions/ExprVectorXYZ.sk deleted file mode 100644 index 2691fa58975..00000000000 --- a/src/test/skript/tests/syntaxes/expressions/ExprVectorXYZ.sk +++ /dev/null @@ -1,16 +0,0 @@ -test "vector xyz": - assert the x component of vector(0, 0, 0) is 0 with "x = 0 vector component failed" - assert the x component of vector(1, 0, 0) is 1 with "x = 1 vector component failed" - assert the y component of vector(1, 1, 0) is 1 with "y = 1 vector component failed" - assert the z component of vector(1, 1, 1) is 1 with "z = 1 vector component failed" - loop 60 times: - set {_x} to a random number between -100 and 100 - set {_y} to a random number between -100 and 100 - set {_z} to a random number between -100 and 100 - set {_vector} to vector({_x}, {_y}, {_z}) - assert the x component of {_vector} is {_x} with "randomly-created vector x equality failed (expected %{_x}%, got %x component of {_vector}%)" - assert the y component of {_vector} is {_y} with "randomly-created vector y equality failed (expected %{_y}%, got %y component of {_vector}%)" - assert the z component of {_vector} is {_z} with "randomly-created vector z equality failed (expected %{_z}%, got %z component of {_vector}%)" - set {_random} to a random vector - set {_vector} to a new vector from x component of {_random}, y component of {_random}, z component of {_random} - assert {_vector} is {_random} with "random vector to created component equality failed (expected %{_random}%, got %{_vector}%)" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprXYZComponent.sk b/src/test/skript/tests/syntaxes/expressions/ExprXYZComponent.sk new file mode 100644 index 00000000000..bb0666c322d --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprXYZComponent.sk @@ -0,0 +1,64 @@ +test "vector xyz": + if running minecraft "1.19.4": + assert the w component of vector(0, 0, 0) is not set with "w vector component return value for vector unexpectedly" + assert the x component of vector(1, 0, 0) is 1 with "x vector component failed" + assert the y component of vector(0, 1, 0) is 1 with "y vector component failed" + assert the z component of vector(0, 0, 1) is 1 with "z vector component failed" + + set {_v} to vector(0,0,0) + set x of {_v} to infinity value + if running minecraft "1.19.4": + assert the w component of {_v} is not set with "set x of vector created w component somehow" + assert the x component of {_v} is infinity value with "set x of vector failed" + assert the y component of {_v} is 0 with "set x of vector modified other components" + assert the z component of {_v} is 0 with "set x of vector modified other components" + + loop 60 times: + set {_x} to a random number between -100 and 100 + set {_y} to a random number between -100 and 100 + set {_z} to a random number between -100 and 100 + set {_vector} to vector({_x}, {_y}, {_z}) + assert the x component of {_vector} is {_x} with "randomly-created vector x equality failed" + assert the y component of {_vector} is {_y} with "randomly-created vector y equality failed" + assert the z component of {_vector} is {_z} with "randomly-created vector z equality failed" + set {_random} to a random vector + set {_vector} to a new vector from x component of {_random}, y component of {_random}, z component of {_random} + assert {_vector} is {_random} with "random vector to created component equality failed (expected %{_random}%, got %{_vector}%)" + + set {_v::*} to vector(1,2,3), vector(2,3,4), and vector(3,4,5) + set x component of {_v::*} to -1.5 + assert the x component of {_v::*} is (-1.5, -1.5, and -1.5) with "x component of multiple vectors failed" + assert the y component of {_v::*} is (2, 3, and 4) with "changing x component of multiple vectors changed y components too" + +test "quaternion wxyz" when running minecraft "1.19.4": + assert the w component of quaternion(1, 0, 0, 0) is 1 with "w vector component failed" + assert the x component of quaternion(1, 0, 0, 0) is 0 with "x vector component failed" + assert the y component of quaternion(1, 0, 0, 0) is 0 with "y vector component failed" + assert the z component of quaternion(1, 0, 0, 0) is 0 with "z vector component failed" + + assert the x component of quaternion(0, 1, 0, 0) is 1 with "x vector component failed" + assert the y component of quaternion(0, 0, 1, 0) is 1 with "y vector component failed" + assert the z component of quaternion(0, 0, 0, 1) is 1 with "z vector component failed" + + set {_q} to quaternion(0,0,0,0) + set x of {_q} to infinity value + assert the w component of {_q} is 0 with "set x of quaternion modified other components" + assert the x component of {_q} is infinity value with "set x of quaternion failed" + assert the y component of {_q} is 0 with "set x of quaternion modified other components" + assert the z component of {_q} is 0 with "set x of quaternion modified other components" + + loop 60 times: + set {_w} to a random number between -100 and 100 + set {_x} to a random number between -100 and 100 + set {_y} to a random number between -100 and 100 + set {_z} to a random number between -100 and 100 + set {_quaternion} to quaternion({_w}, {_x}, {_y}, {_z}) + assert the w component of {_quaternion} is {_w} with "randomly-created quaternion w equality failed" + assert the x component of {_quaternion} is {_x} with "randomly-created quaternion x equality failed" + assert the y component of {_quaternion} is {_y} with "randomly-created quaternion y equality failed" + assert the z component of {_quaternion} is {_z} with "randomly-created quaternion z equality failed" + + set {_v::*} to quaternion(0,1,2,3), quaternion(1,2,3,4), and quaternion(2,3,4,5) + set x component of {_v::*} to -1.5 + assert the x component of {_v::*} is (-1.5, -1.5, and -1.5) with "x component of multiple quaternions failed" + assert the y component of {_v::*} is (2, 3, and 4) with "changing x component of multiple quaternions changed y components too" diff --git a/src/test/skript/tests/syntaxes/functions/quaternions.sk b/src/test/skript/tests/syntaxes/functions/quaternions.sk new file mode 100644 index 00000000000..892d24006aa --- /dev/null +++ b/src/test/skript/tests/syntaxes/functions/quaternions.sk @@ -0,0 +1,23 @@ +test "quaternions" when running minecraft "1.19": + set {_a} to quaternion(1, 2, 3, 4) + assert w of {_a} is 1 with error "failed to get w component of quaternion" + assert x of {_a} is 2 with error "failed to get x component of quaternion" + assert y of {_a} is 3 with error "failed to get y component of quaternion" + assert z of {_a} is 4 with error "failed to get z component of quaternion" + + + set {_a} to quaternion(-1.2, 0.0, -infinity value, NaN value) + assert w of {_a} is -1.2 with error "failed to get w component of quaternion" + assert x of {_a} is 0 with error "failed to get x component of quaternion" + assert y of {_a} is -infinity value with error "failed to get y component of quaternion" + assert isNaN(z of {_a}) is true with error "failed to get z component of quaternion" + +test "axis angle" when running minecraft "1.19": + set {_a} to axisAngle(100, vector(2, 3, 4)) + assert w of {_a} is 0.642787516117096 with error "failed to get w component of axis angle" + assert x of {_a} is 1.5320889949798584 with error "failed to get x component of axis angle" + assert y of {_a} is 2.298133373260498 with error "failed to get y component of axis angle" + assert z of {_a} is 3.064177989959717 with error "failed to get z component of axis angle" + + set {_a} to axisAngle(-1.2, vector(0.0, -infinity value, NaN value)) + assert {_a} is not set with error "invalid axis angle was set"