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 33c4be3fdd1..644004b8d3a 100644
--- a/src/main/java/ch/njol/skript/classes/data/JavaClasses.java
+++ b/src/main/java/ch/njol/skript/classes/data/JavaClasses.java
@@ -1,21 +1,3 @@
-/**
- * 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 ch.njol.skript.Skript;
@@ -33,16 +15,65 @@
import ch.njol.skript.util.Utils;
import ch.njol.util.StringUtils;
import ch.njol.yggdrasil.Fields;
+import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.joml.Quaternionf;
+import java.util.function.Function;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavaClasses {
public static final int VARIABLENAME_NUMBERACCURACY = 8;
- public static final Pattern INTEGER_PATTERN = Pattern.compile("-?\\d+(_\\d+)*");
- public static final Pattern NUMBER_PATTERN = Pattern.compile("-?\\d+(_\\d+)*(?>\\.\\d+(_\\d+)*)?%?");
+
+ /**
+ * The format of an integer.
+ *
+ * Has an optional negative sign and may contain one underscores followed by any number of digits.
+ *
+ */
+ public static final String INTEGER_NUMBER_PATTERN = "-?\\d+(_\\d+)*";
+
+ /**
+ * Matches an integer with an optional unit of radians or degrees.
+ *
+ * First, the actual number format {@code num} is specified. Then, an optional angle unit is specified.
+ * For this, the {@code rad} group is used. This specifies that the number is in radians.
+ * This is used to determine if the number should be converted to degrees.
+ * Degrees is not a named group because it just returns the value in the {@code num} group, which
+ * is the default behaviour.
+ * Optionally, the user can use {@code x in degrees} instead of {@code x degrees}.
+ *
+ */
+ public static final Pattern INTEGER_PATTERN =
+ Pattern.compile("(?" + INTEGER_NUMBER_PATTERN + ")" +
+ "(?: (?:in )?(?:(?rad(?:ian)?s?)|deg(?:ree)?s?))?");
+
+ /**
+ * The format of a decimal number.
+ *
+ * Has an optional negative sign and may contain one underscores followed by any number of digits,
+ * in the whole part or the fractional part. The fractional part is optional. May be followed by a percentage sign,
+ * to indicate that the number is a percentage.
+ *
+ */
+ public static final String DECIMAL_NUMBER_PATTERN = "-?\\d+(_\\d+)*(?>\\.\\d+(_\\d+)*)?%?";
+
+ /**
+ * Matches a decimal number with an optional unit of radians or degrees.
+ *
+ * First, the actual number format {@code num} is specified. Then, an optional angle unit is specified.
+ * For this, the {@code rad} group is used. This specifies that the number is in radians.
+ * This is used to determine if the number should be converted to degrees.
+ * Degrees is not a named group because it just returns the value in the {@code num} group, which
+ * is the default behaviour.
+ * Optionally, the user can use {@code x in degrees} instead of {@code x degrees}.
+ *
+ */
+ public static final Pattern DECIMAL_PATTERN =
+ Pattern.compile("(?" + DECIMAL_NUMBER_PATTERN + ")" +
+ "(?: (?:in )?(?:(?rad(?:ian)?s?)|deg(?:ree)?s?))?");
static {
Classes.registerClass(new ClassInfo<>(Object.class, "object")
@@ -52,328 +83,55 @@ public class JavaClasses {
.usage("")
.examples("")
.since("1.0"));
-
+
Classes.registerClass(new ClassInfo<>(Number.class, "number")
.user("num(ber)?s?")
.name("Number")
- .description("A number, e.g. 2.5, 3, or -9812454.",
- "Please note that many expressions only need integers, i.e. will discard any fractional parts of any numbers without producing an error.")
- .usage("[-]###[.###] (any amount of digits; very large numbers will be truncated though)")
- .examples("set the player's health to 5.5",
- "set {_temp} to 2*{_temp} - 2.5")
+ .description(
+ "A number, e.g. 2.5, 3, -9812454, 30 degrees or 3.14 radians.",
+ "Please note that many expressions only need integers, i.e. " +
+ "will discard any fractional parts of any numbers without producing an error.",
+ "Radians will be converted to degrees.")
+ .usage("[-]###[.###] [[in ](rad[ian][s]|deg[ree][s])] (any amount of digits; very large numbers will be truncated though)")
+ .examples(
+ "set the player's health to 5.5",
+ "set {_temp} to 2*{_temp} - 2.5",
+ "set {_angle} to 3.14 in radians # will be converted to degrees"
+ )
.since("1.0")
// is registered after all other number classes
.defaultExpression(new SimpleLiteral<>(1, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Number parse(String s, ParseContext context) {
- if (!NUMBER_PATTERN.matcher(s).matches())
- return null;
- if (INTEGER_PATTERN.matcher(s).matches()) {
- try {
- return Long.valueOf(s.replace("_", ""));
- } catch (NumberFormatException ignored) { }
- }
- try {
- s = s.replace("_", "");
+ .parser(new NumberParser())
+ .serializer(new NumberSerializer()));
- Double d = s.endsWith("%") ? Double.parseDouble(s.substring(0, s.length() - 1)) / 100 : Double.parseDouble(s);
- if (d.isNaN() || d.isInfinite())
- return null;
- return d;
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Number n, int flags) {
- return StringUtils.toString(n.doubleValue(), SkriptConfig.numberAccuracy.value());
- }
-
- @Override
- public String toVariableNameString(Number n) {
- return StringUtils.toString(n.doubleValue(), VARIABLENAME_NUMBERACCURACY);
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Number n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Number o, Fields f) {
- assert false;
- }
-
- @Override
- @Nullable
- public Number deserialize(String s) {
- try {
- return Integer.valueOf(s);
- } catch (NumberFormatException ignored) {}
- try {
- return Double.valueOf(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Long.class, "long")
.user("int(eger)?s?")
.name(ClassInfo.NO_DOC)
.before("integer", "short", "byte")
.defaultExpression(new SimpleLiteral<>((long) 1, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Long parse(String s, ParseContext context) {
- if (!INTEGER_PATTERN.matcher(s).matches())
- return null;
- try {
- return Long.valueOf(s.replace("_", ""));
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Long l, int flags) {
- return "" + l;
- }
-
- @Override
- public String toVariableNameString(Long l) {
- return "" + l;
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Long n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Long o, Fields f) {
- assert false;
- }
+ .parser(new LongParser())
+ .serializer(new LongSerializer()));
- @Override
- @Nullable
- public Long deserialize(String s) {
- try {
- return Long.parseLong(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Integer.class, "integer")
.name(ClassInfo.NO_DOC)
.defaultExpression(new SimpleLiteral<>(1, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Integer parse(String s, ParseContext context) {
- if (!INTEGER_PATTERN.matcher(s).matches())
- return null;
- try {
- return Integer.valueOf(s.replace("_", ""));
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Integer i, int flags) {
- return "" + i;
- }
-
- @Override
- public String toVariableNameString(Integer i) {
- return "" + i;
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Integer n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Integer o, Fields f) {
- assert false;
- }
+ .parser(new IntegerParser())
+ .serializer(new IntegerSerializer()));
- @Override
- @Nullable
- public Integer deserialize(String s) {
- try {
- return Integer.parseInt(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Double.class, "double")
.name(ClassInfo.NO_DOC)
.defaultExpression(new SimpleLiteral<>(1., true))
.after("long")
.before("float", "integer", "short", "byte")
- .parser(new Parser() {
- @Override
- @Nullable
- public Double parse(String s, ParseContext context) {
- if (!NUMBER_PATTERN.matcher(s).matches())
- return null;
- try {
- s = s.replace("_", "");
-
- Double d = s.endsWith("%") ? Double.parseDouble(s.substring(0, s.length() - 1)) / 100 : Double.parseDouble(s);
- if (d.isNaN() || d.isInfinite())
- return null;
- return d;
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Double d, int flags) {
- return StringUtils.toString(d, SkriptConfig.numberAccuracy.value());
- }
-
- @Override
- public String toVariableNameString(Double d) {
- return StringUtils.toString(d, VARIABLENAME_NUMBERACCURACY);
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Double n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Double o, Fields f) {
- assert false;
- }
+ .parser(new DoubleParser())
+ .serializer(new DoubleSerializer()));
- @Override
- @Nullable
- public Double deserialize(String s) {
- try {
- return Double.parseDouble(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Float.class, "float")
.name(ClassInfo.NO_DOC)
.defaultExpression(new SimpleLiteral<>(1f, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Float parse(String s, ParseContext context) {
- if (!NUMBER_PATTERN.matcher(s).matches())
- return null;
- try {
- s = s.replace("_", "");
-
- Float f = s.endsWith("%") ? Float.parseFloat(s.substring(0, s.length() - 1)) / 100 : Float.parseFloat(s);
- if (f.isNaN() || f.isInfinite()) {
- return null;
- }
- return f;
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Float f, int flags) {
- return StringUtils.toString(f, SkriptConfig.numberAccuracy.value());
- }
-
- @Override
- public String toVariableNameString(Float f) {
- return StringUtils.toString(f.doubleValue(), VARIABLENAME_NUMBERACCURACY);
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Float n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Float o, Fields f) {
- assert false;
- }
+ .parser(new FloatParser())
+ .serializer(new FloatSerializer()));
- @Override
- @Nullable
- public Float deserialize(String s) {
- try {
- return Float.parseFloat(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Boolean.class, "boolean")
.user("booleans?")
.name("Boolean")
@@ -381,10 +139,10 @@ public boolean mustSyncDeserialization() {
.usage("true/yes/on or false/no/off")
.examples("set {config.%player%.use mod} to false")
.since("1.0")
- .parser(new Parser() {
+ .parser(new Parser<>() {
private final RegexMessage truePattern = new RegexMessage("boolean.true.pattern");
private final RegexMessage falsePattern = new RegexMessage("boolean.false.pattern");
-
+
@Override
@Nullable
public Boolean parse(String s, ParseContext context) {
@@ -394,30 +152,30 @@ public Boolean parse(String s, ParseContext context) {
return Boolean.FALSE;
return null;
}
-
+
private final Message trueName = new Message("boolean.true.name");
private final Message falseName = new Message("boolean.false.name");
-
+
@Override
public String toString(Boolean b, int flags) {
return b ? trueName.toString() : falseName.toString();
}
-
+
@Override
public String toVariableNameString(Boolean b) {
return "" + b;
}
- }).serializer(new Serializer() {
+ }).serializer(new Serializer() {
@Override
public Fields serialize(Boolean n) {
throw new IllegalStateException(); // serialised natively by Yggdrasil
}
-
+
@Override
public boolean canBeInstantiated() {
return true;
}
-
+
@Override
public void deserialize(Boolean o, Fields f) {
assert false;
@@ -432,127 +190,25 @@ public Boolean deserialize(String s) {
return Boolean.FALSE;
return null;
}
-
+
@Override
public boolean mustSyncDeserialization() {
return false;
}
}));
-
+
Classes.registerClass(new ClassInfo<>(Short.class, "short")
.name(ClassInfo.NO_DOC)
.defaultExpression(new SimpleLiteral<>((short) 1, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Short parse(String s, ParseContext context) {
- if (!INTEGER_PATTERN.matcher(s).matches())
- return null;
- try {
- return Short.valueOf(s.replace("_", ""));
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Short s, int flags) {
- return "" + s;
- }
-
- @Override
- public String toVariableNameString(Short s) {
- return "" + s;
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Short n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Short o, Fields f) {
- assert false;
- }
+ .parser(new ShortParser())
+ .serializer(new ShortSerializer()));
- @Override
- @Nullable
- public Short deserialize(String s) {
- try {
- return Short.parseShort(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(Byte.class, "byte")
.name(ClassInfo.NO_DOC)
.defaultExpression(new SimpleLiteral<>((byte) 1, true))
- .parser(new Parser() {
- @Override
- @Nullable
- public Byte parse(String s, ParseContext context) {
- if (!INTEGER_PATTERN.matcher(s).matches())
- return null;
- try {
- return Byte.valueOf(s.replace("_", ""));
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public String toString(Byte b, int flags) {
- return "" + b;
- }
-
- @Override
- public String toVariableNameString(Byte b) {
- return "" + b;
- }
- }).serializer(new Serializer() {
- @Override
- public Fields serialize(Byte n) {
- throw new IllegalStateException(); // serialised natively by Yggdrasil
- }
-
- @Override
- public boolean canBeInstantiated() {
- return true;
- }
-
- @Override
- public void deserialize(Byte o, Fields f) {
- assert false;
- }
+ .parser(new ByteParser())
+ .serializer(new ByteSerializer()));
- @Override
- @Nullable
- public Byte deserialize(String s) {
- try {
- return Byte.parseByte(s);
- } catch (NumberFormatException e) {
- return null;
- }
- }
-
- @Override
- public boolean mustSyncDeserialization() {
- return false;
- }
- }));
-
Classes.registerClass(new ClassInfo<>(String.class, "string")
.user("(text|string)s?")
.name("Text")
@@ -568,7 +224,7 @@ public boolean mustSyncDeserialization() {
"message \"Hello %player%\"",
"message \"The id of \"\"%type of tool%\"\" is %id of tool%.\"")
.since("1.0")
- .parser(new Parser() {
+ .parser(new Parser<>() {
@Override
@Nullable
public String parse(String s, ParseContext context) {
@@ -590,37 +246,37 @@ public String parse(String s, ParseContext context) {
assert false;
return null;
}
-
+
@Override
public boolean canParse(ParseContext context) {
return context != ParseContext.DEFAULT;
}
-
+
@Override
public String toString(String s, int flags) {
return s;
}
-
+
@Override
public String getDebugMessage(String s) {
return '"' + s + '"';
}
-
+
@Override
public String toVariableNameString(String s) {
return s;
}
- }).serializer(new Serializer() {
+ }).serializer(new Serializer() {
@Override
public Fields serialize(String n) {
throw new IllegalStateException(); // serialised natively by Yggdrasil
}
-
+
@Override
public boolean canBeInstantiated() {
return true;
}
-
+
@Override
public void deserialize(String o, Fields f) {
assert false;
@@ -630,7 +286,7 @@ public void deserialize(String o, Fields f) {
public String deserialize(String s) {
return s;
}
-
+
@Override
public boolean mustSyncDeserialization() {
return false;
@@ -640,35 +296,500 @@ public boolean mustSyncDeserialization() {
// 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;
- }
+ .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 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;
- }
- }));
+ @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;
+ }
+ }));
+ }
+
+ /**
+ * Converts a string to a number formatted as an integer.
+ *
+ * Applies {@code stringToNumber} for parsing the number, then tries to
+ * convert radians to degrees if the string contains a radian group.
+ *
+ *
+ * @param string The string with the possible number.
+ * @param stringToNumber The function to parse the number, e.g. {@link Integer#parseInt(String)}.
+ * @return The parsed string, or null if the string could not be parsed.
+ */
+ @Contract(pure = true)
+ private static @Nullable T convertIntegerFormatted(
+ String string,
+ Function stringToNumber
+ ) {
+ Matcher matcher = INTEGER_PATTERN.matcher(string);
+
+ if (!matcher.matches())
+ return null;
+
+ String number = matcher.group("num").replace("_", "");
+ if (matcher.group("rad") != null) {
+ try {
+ //noinspection unchecked
+ return (T) (Double) Math.toDegrees(stringToNumber.apply(number).doubleValue());
+ } catch (NumberFormatException ignored) {
+ }
+ } else {
+ try {
+ return stringToNumber.apply(number);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Converts a string to a number formatted as a decimal.
+ *
+ * Applies {@code stringToNumber} for parsing the number.
+ * If the number is a percentage, it gets parsed using the double value divided by 100, and {@code fromDouble}.
+ * Then tries to convert radians to degrees if the string contains a radian group.
+ *
+ *
+ * @param string The string with the possible number.
+ * @param stringToNumber The function to parse the number, e.g. {@link Integer#parseInt(String)}.
+ * @return The parsed string, or null if the string could not be parsed.
+ */
+ @Contract(pure = true)
+ private static @Nullable T convertDecimalFormatted(
+ String string,
+ Function stringToNumber
+ ) {
+ Matcher matcher = DECIMAL_PATTERN.matcher(string);
+
+ if (!matcher.matches())
+ return null;
+
+ String number = matcher.group("num").replace("_", "");
+ try {
+ T result;
+ if (number.endsWith("%")) {
+ T extracted = stringToNumber.apply(number.substring(0, number.length() - 1));
+ //noinspection unchecked
+ result = (T) (Double) (extracted.doubleValue() / 100.0);
+ } else {
+ result = stringToNumber.apply(number);
+ }
+
+ if (matcher.group("rad") != null) {
+ try {
+ //noinspection unchecked
+ return (T) (Double) Math.toDegrees(result.doubleValue());
+ } catch (NumberFormatException ignored) {
+ }
+ }
+
+ return result;
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ private static class NumberParser extends Parser {
+
+ @Override
+ public @Nullable Number parse(String string, ParseContext context) {
+ Matcher numberMatcher = DECIMAL_PATTERN.matcher(string);
+ if (!numberMatcher.matches())
+ return null;
+
+ Integer integerAttempt = convertIntegerFormatted(string, Integer::parseInt);
+ if (integerAttempt != null)
+ return integerAttempt;
+
+ Double parsed = convertDecimalFormatted(string, Double::parseDouble);
+ return parsed == null || parsed.isInfinite() || parsed.isNaN() ? null : parsed;
+ }
+
+ @Override
+ public String toString(Number number, int flags) {
+ return StringUtils.toString(number.doubleValue(), SkriptConfig.numberAccuracy.value());
+ }
+
+ @Override
+ public String toVariableNameString(Number number) {
+ return StringUtils.toString(number.doubleValue(), VARIABLENAME_NUMBERACCURACY);
+ }
+
+ }
+
+ private static class NumberSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Number number) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Number number, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Number deserialize(String string) {
+ try {
+ return Integer.valueOf(string);
+ } catch (NumberFormatException ignored) {
+ }
+ try {
+ return Double.valueOf(string);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class LongParser extends Parser {
+
+ @Override
+ public @Nullable Long parse(String string, ParseContext context) {
+ return convertIntegerFormatted(string, Long::parseLong);
+ }
+
+ @Override
+ public String toString(Long l, int flags) {
+ return l.toString();
+ }
+
+ @Override
+ public String toVariableNameString(Long l) {
+ return l.toString();
+ }
+
+ }
+
+ private static class LongSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Long l) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Long l, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Long deserialize(String string) {
+ try {
+ return Long.parseLong(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class IntegerParser extends Parser {
+
+ @Override
+ public @Nullable Integer parse(String string, ParseContext context) {
+ return convertIntegerFormatted(string, Integer::parseInt);
+ }
+
+ @Override
+ public String toString(Integer i, int flags) {
+ return i.toString();
+ }
+
+ @Override
+ public String toVariableNameString(Integer i) {
+ return i.toString();
+ }
+
+ }
+
+ private static class IntegerSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Integer i) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Integer i, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Integer deserialize(String string) {
+ try {
+ return Integer.parseInt(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class DoubleParser extends Parser {
+
+ @Override
+ public @Nullable Double parse(String string, ParseContext context) {
+ Double parsed = convertDecimalFormatted(string, Double::parseDouble);
+
+ return parsed == null || parsed.isInfinite() || parsed.isNaN() ? null : parsed;
+ }
+
+ @Override
+ public String toString(Double d, int flags) {
+ return StringUtils.toString(d, SkriptConfig.numberAccuracy.value());
+ }
+
+ @Override
+ public String toVariableNameString(Double d) {
+ return StringUtils.toString(d, VARIABLENAME_NUMBERACCURACY);
+ }
+
+ }
+
+ private static class DoubleSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Double d) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Double d, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Double deserialize(String string) {
+ try {
+ return Double.parseDouble(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class FloatParser extends Parser {
+
+ @Override
+ public @Nullable Float parse(String string, ParseContext context) {
+ Float parsed = convertDecimalFormatted(string, Float::parseFloat);
+
+ return parsed == null || parsed.isInfinite() || parsed.isNaN() ? null : parsed;
+ }
+
+ @Override
+ public String toString(Float f, int flags) {
+ return StringUtils.toString(f, SkriptConfig.numberAccuracy.value());
+ }
+
+ @Override
+ public String toVariableNameString(Float f) {
+ return StringUtils.toString(f.doubleValue(), VARIABLENAME_NUMBERACCURACY);
+ }
+
+ }
+
+ private static class FloatSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Float f) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Float f, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Float deserialize(String string) {
+ try {
+ return Float.parseFloat(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class ShortParser extends Parser {
+
+ @Override
+ public @Nullable Short parse(String string, ParseContext context) {
+ return convertIntegerFormatted(string, Short::parseShort);
+ }
+
+ @Override
+ public String toString(Short s, int flags) {
+ return s.toString();
+ }
+
+ @Override
+ public String toVariableNameString(Short s) {
+ return s.toString();
+ }
+
+ }
+
+ private static class ShortSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Short s) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Short s, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Short deserialize(String string) {
+ try {
+ return Short.parseShort(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
+
+ }
+
+ private static class ByteParser extends Parser {
+
+ @Override
+ public @Nullable Byte parse(String string, ParseContext context) {
+ return convertIntegerFormatted(string, Byte::parseByte);
+ }
+
+ @Override
+ public String toString(Byte b, int flags) {
+ return b.toString();
+ }
+
+ @Override
+ public String toVariableNameString(Byte b) {
+ return b.toString();
+ }
+
+ }
+
+ private static class ByteSerializer extends Serializer {
+
+ @Override
+ public Fields serialize(Byte b) {
+ throw new IllegalStateException(); // serialised natively by Yggdrasil
+ }
+
+ @Override
+ public boolean canBeInstantiated() {
+ return true;
+ }
+
+ @Override
+ public void deserialize(Byte b, Fields fields) {
+ assert false;
+ }
+
+ @Override
+ public @Nullable Byte deserialize(String string) {
+ try {
+ return Byte.parseByte(string);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean mustSyncDeserialization() {
+ return false;
+ }
}
diff --git a/src/main/java/ch/njol/skript/effects/EffContinue.java b/src/main/java/ch/njol/skript/effects/EffContinue.java
index b83fefec4f2..3c3fb33a407 100644
--- a/src/main/java/ch/njol/skript/effects/EffContinue.java
+++ b/src/main/java/ch/njol/skript/effects/EffContinue.java
@@ -1,21 +1,3 @@
-/**
- * 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 ch.njol.skript.Skript;
@@ -60,7 +42,7 @@ public class EffContinue extends Effect {
static {
Skript.registerEffect(EffContinue.class,
"continue [this loop|[the] [current] loop]",
- "continue [the] <" + JavaClasses.INTEGER_PATTERN + ">(st|nd|rd|th) loop"
+ "continue [the] <" + JavaClasses.INTEGER_NUMBER_PATTERN + ">(st|nd|rd|th) loop"
);
}
diff --git a/src/main/java/ch/njol/skript/effects/EffExit.java b/src/main/java/ch/njol/skript/effects/EffExit.java
index 264f466becb..7d9a2ac61ec 100644
--- a/src/main/java/ch/njol/skript/effects/EffExit.java
+++ b/src/main/java/ch/njol/skript/effects/EffExit.java
@@ -1,21 +1,3 @@
-/**
- * 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 ch.njol.skript.Skript;
@@ -33,19 +15,18 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
-import java.util.ArrayList;
import java.util.List;
@Name("Exit")
@Description("Exits a given amount of loops and conditionals, or the entire trigger.")
@Examples({
"if player has any ore:",
- "\tstop",
+ "\tstop",
"message \"%player% has no ores!\"",
"loop blocks above the player:",
- "\tloop-block is not air:",
- "\t\texit 2 sections",
- "\tset loop-block to water"
+ "\tloop-block is not air:",
+ "\t\texit 2 sections",
+ "\tset loop-block to water"
})
@Since("unknown (before 2.1)")
public class EffExit extends Effect {
@@ -54,7 +35,7 @@ public class EffExit extends Effect {
Skript.registerEffect(EffExit.class,
"(exit|stop) [trigger]",
"(exit|stop) [1|a|the|this] (section|1:loop|2:conditional)",
- "(exit|stop) <" + JavaClasses.INTEGER_PATTERN + "> (section|1:loop|2:conditional)s",
+ "(exit|stop) <" + JavaClasses.INTEGER_NUMBER_PATTERN + "> (section|1:loop|2:conditional)s",
"(exit|stop) all (section|1:loop|2:conditional)s");
}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprAngle.java b/src/main/java/ch/njol/skript/expressions/ExprAngle.java
new file mode 100644
index 00000000000..7916f8fd405
--- /dev/null
+++ b/src/main/java/ch/njol/skript/expressions/ExprAngle.java
@@ -0,0 +1,77 @@
+package ch.njol.skript.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.jetbrains.annotations.Nullable;
+
+@Name("Angle")
+@Description({
+ "Represents the passed number value in degrees.",
+ "If radians is specified, converts the passed value to degrees. This conversion may not be entirely accurate, " +
+ "due to floating point precision.",
+})
+@Examples({
+ "set {_angle} to 90 degrees",
+ "{_angle} is 90 # true",
+ "180 degrees is pi # true",
+ "pi radians is 180 degrees # true"
+})
+@Since("INSERT VERSION")
+public class ExprAngle extends SimpleExpression {
+
+ static {
+ Skript.registerExpression(ExprAngle.class, Number.class, ExpressionType.SIMPLE,
+ "%number% [in] deg[ree][s]",
+ "%number% [in] rad[ian][s]",
+ "%numbers% in deg[ree][s]",
+ "%numbers% in rad[ian][s]");
+ }
+
+ private Expression angle;
+ private boolean isRadians;
+
+ @Override
+ public boolean init(Expression>[] expressions, int matchedPattern,
+ Kleenean isDelayed, ParseResult parseResult) {
+ //noinspection unchecked
+ angle = (Expression) expressions[0];
+ isRadians = matchedPattern % 2 != 0;
+ return true;
+ }
+
+ @Override
+ protected Number @Nullable [] get(Event event) {
+ Number[] numbers = angle.getArray(event);
+
+ if (isRadians)
+ for (int i = 0; i < numbers.length; i++)
+ numbers[i] = Math.toDegrees(numbers[i].doubleValue());
+
+ return numbers;
+ }
+
+ @Override
+ public boolean isSingle() {
+ return angle.isSingle();
+ }
+
+ @Override
+ public Class extends Number> getReturnType() {
+ return Number.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return angle.toString(event, debug) + " in " + (isRadians ? "degrees" : "radians");
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/hooks/economy/classes/Money.java b/src/main/java/ch/njol/skript/hooks/economy/classes/Money.java
index bc4d550dabf..dafe28d61e6 100644
--- a/src/main/java/ch/njol/skript/hooks/economy/classes/Money.java
+++ b/src/main/java/ch/njol/skript/hooks/economy/classes/Money.java
@@ -158,7 +158,7 @@ private static Money parseMoney(String s, String addition) {
@Nullable
private static Double parseDouble(String s) {
- if (!JavaClasses.NUMBER_PATTERN.matcher(s).matches())
+ if (!JavaClasses.DECIMAL_PATTERN.matcher(s).matches())
return null;
try {
return Double.parseDouble(s);
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
index 14ed2fc1737..7a560456640 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/misc/effects/EffRotate.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/effects/EffRotate.java
@@ -50,9 +50,9 @@ 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 %vectors/quaternions/displays% around [the] [global] (:x|:y|:z)(-| )axis by %number%",
+ "rotate %quaternions/displays% around [the|its|their] local (:x|:y|:z)(-| )ax(i|e)s by %number%",
+ "rotate %vectors/quaternions/displays% around [the] %vector% by %number%",
"rotate %quaternions/displays% by x %number%, y %number%(, [and]| and) z %number%"
);
}
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
index ee60bf91907..eb5cfaffab9 100644
--- a/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprRotate.java
+++ b/src/main/java/org/skriptlang/skript/bukkit/misc/expressions/ExprRotate.java
@@ -46,9 +46,9 @@ public class ExprRotate extends SimpleExpression