Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

String Colors #7276

Open
wants to merge 13 commits into
base: dev/feature
Choose a base branch
from
37 changes: 0 additions & 37 deletions src/main/java/ch/njol/skript/expressions/ExprLastColor.java

This file was deleted.

209 changes: 209 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprStringColor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
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.expressions.base.PropertyExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.SyntaxStringBuilder;
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.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

@Name("String Colors")
@Description({
"Retrieve the first, the last, or all of the color objects or color codes of a string.",
"The retrieved color codes of the string will be formatted with the color symbol."
})
@Examples({
"set {_colors::*} to the string colors of \"<red>hey<blue>yo\"",
"",
"set {_color} to the first string color code of \"&aGoodbye!\"",
"send \"%{_color}%Howdy!\" to all players"
})
@Since("INSERT VERSION")
public class ExprStringColor extends PropertyExpression<String, Object> {

private enum StringColor {
ALL, FIRST, LAST
}

private static final StringColor[] STRING_COLORS = StringColor.values();

static {
Skript.registerExpression(ExprStringColor.class, Object.class, ExpressionType.PROPERTY,
"[all [of the|the]|the] string colo[u]r[s] [code:code[s]] of %strings%",
"[the] first string colo[u]r[s] [code:code[s]] of %strings%",
"[the] last string colo[u]r[s] [code:code[s]] of %strings%");
}

private StringColor selectedState;
private boolean getCodes;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
selectedState = STRING_COLORS[matchedPattern];
getCodes = parseResult.hasTag("code");
//noinspection unchecked
setExpr((Expression<String>) exprs[0]);
return true;
}

@Override
protected Object @Nullable [] get(Event event, String[] source) {
List<Object> colors = new ArrayList<>();
for (String string : getExpr().getArray(event)) {
List<Object> stringColors = getColors(string);
if (stringColors.isEmpty())
continue;
colors.addAll(stringColors);
}
return colors.toArray(new Object[0]);
}

@Override
public Class<?> getReturnType() {
return getCodes ? String.class : Color.class;
}

@Override
public Class<?>[] possibleReturnTypes() {
return CollectionUtils.array(String.class, SkriptColor.class, ColorRGB.class);
}

@Override
public boolean isSingle() {
if (selectedState != StringColor.ALL && getExpr().isSingle())
return true;
return false;
}

@Override
public String toString(@Nullable Event event, boolean debug) {
SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug);
builder.append(switch (selectedState) {
case ALL -> "all of the";
case FIRST -> "the first";
case LAST -> "the last";
});
if (getCodes)
builder.append("string color codes");
else
builder.append("string colors");
builder.append("of", getExpr());
return builder.toString();
}

private List<Object> getColors(String string) {
List<Object> colors = new ArrayList<>();
int length = string.length();
Object last = null;
for (int index = 0; index < length; index++) {
if (string.charAt(index) == '§') {
boolean checkHex = checkHex(string, index);
SkriptColor checkChar = SkriptColor.fromColorChar(string.charAt(index + 1));
if (checkHex) {
// Hex colors contain 14 chars, "§x" indicating the following 12 characters will be for the hex.
// Then the following chars of the hex, ex: ff0000 = §f§f§0§0§0§0
// Currently 'index' is '§' from the '§x' indicator.
// Adding + 14 to the substring, will get the full hex: §x§f§f§0§0§0§0
String hexString = string.substring(index, index + 14);
Object result;
if (getCodes) {
result = hexString;
} else {
result = fromHex(hexString);
}
last = result;
colors.add(result);
if (selectedState == StringColor.FIRST)
break;
// Adding 13 to the index, because it will add 1 after this cycle is done
index += 13;
} else if (checkChar != null) {
// Character colors are vanilla colors such as §a, §b, §c etc.
// Currently, 'index' is '§'
String colorString = string.substring(index, index + 2);
Object result;
if (getCodes) {
result = colorString;
} else {
result = checkChar;
}
colors.add(result);
last = result;
if (selectedState == StringColor.FIRST)
break;
// Adding 1 to the index, because it will add 1 after this cycle is done
index += 1;
}
}
}
if (selectedState == StringColor.LAST) {
colors.clear();
colors.add(last);
}
return colors;
}

private boolean checkHex(String string, int index) {
int length = string.length();
if (length < index + 12)
return false;
if (string.charAt(index + 1) != 'x')
return false;

for (int i = index + 2; i <= index; i += 2) {
if (string.charAt(i) != '§')
return false;
}

for (int i = index + 3; i <= index; i += 2) {
char toCheck = string.charAt(i);
if (toCheck < '0' || toCheck > 'f')
return false;
if (toCheck > '9' && toCheck < 'A')
return false;
if (toCheck > 'F' && toCheck < 'a')
return false;
}

return true;
}

private ColorRGB fromHex(@NotNull String hex) {
if (hex.startsWith("§x"))
hex = hex.substring(2);
hex = hex.replaceAll("§", "");

int length = hex.length();
int alpha = 255, red, green, blue;

if (length == 6) {
red = Integer.parseInt(hex.substring(0, 2), 16);
green = Integer.parseInt(hex.substring(2, 4), 16);
blue = Integer.parseInt(hex.substring(4, 6), 16);
} else if (length == 8) {
alpha = Integer.parseInt(hex.substring(0, 2), 16);
red = Integer.parseInt(hex.substring(2, 4), 16);
green = Integer.parseInt(hex.substring(4, 6), 16);
blue = Integer.parseInt(hex.substring(6, 8), 16);
} else {
throw new UnsupportedOperationException("Unsupported hex format - requires #RRGGBB or #AARRGGBB");
}
return ColorRGB.fromRGBA(red, green, blue, alpha);
}

}
11 changes: 11 additions & 0 deletions src/main/java/ch/njol/skript/util/SkriptColor.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public enum SkriptColor implements Color {
private final static Map<String, SkriptColor> names = new HashMap<>();
private final static Set<SkriptColor> colors = new HashSet<>();
private final static String LANGUAGE_NODE = "colors";
private final static Map<Character, SkriptColor> BY_CHAR = new HashMap<>();

static {
colors.addAll(Arrays.asList(values()));
Expand All @@ -57,6 +58,7 @@ public enum SkriptColor implements Color {
color.setAdjective(new Adjective(node + ".adjective"));
for (String name : Language.getList(node + ".names"))
names.put(name.toLowerCase(Locale.ENGLISH), color);
BY_CHAR.put(color.asChatColor().getChar(), color);
}
});
}
Expand Down Expand Up @@ -231,6 +233,15 @@ public static String replaceColorChar(String s) {
return s.replace('\u00A7', '&');
}

/**
* Retrieve a {@link SkriptColor} correlating to the color character from {@code character}
* @param character
* @return The resulting {@link SkriptColor}
*/
public static @Nullable SkriptColor fromColorChar(char character) {
return BY_CHAR.get(character);
}

@Override
public String toString() {
return adjective == null ? "" + name() : adjective.toString(-1, 0);
Expand Down
16 changes: 16 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprStringColors.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
test "string colors":
set {_colorcodes::*} to "&a", "&b", "&c", "<#ff0000>", "<#03d7fc>" and "<#48ff05>"
set {_colors::*} to light green, light cyan, pink, rgb(255, 0, 0), rgb(3, 215, 252) and rgb(72, 255, 5)
set {_string} to join {_colorcodes::*}

loop the string color codes of {_string}:
assert {_colorcodes::*} contains loop-value with "Unexpected Color Code"

assert the first string color code of {_string} is {_colorcodes::1} with "First string color code does not match"
assert the last string color code of {_string} is {_colorcodes::6} with "Last string color code does not match"

loop the string colors of {_string}:
assert {_colors::*} contains loop-value with "Unexpected Color: %loop-value%"

assert the first string color of {_string} is {_colors::1} with "First string color does not match"
assert the last string color of {_string} is {_colors::6} with "Last string color does not match"
Loading