Files
MagicGem-Reforged/src/main/java/cn/infstar/magicgem/expression/DynamicExpression.java
T

147 lines
6.5 KiB
Java

package cn.infstar.magicgem.expression;
import cn.infstar.magicgem.integration.PlaceholderBridge;
import cn.infstar.magicgem.util.EnchantmentResolver;
import de.tr7zw.changeme.nbtapi.NBT;
import org.bukkit.ChatColor;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class DynamicExpression {
private static final Pattern ITEM_VARIABLE = Pattern.compile("\\$(LORE|WORD|NBT|ENCHANT):([^?$]+?)(?:\\?([^$]*))?\\$", Pattern.CASE_INSENSITIVE);
private static final Pattern NUMBER = Pattern.compile("[-+]?\\d+(?:\\.\\d+)?");
private final Expression expression;
private final PlaceholderBridge placeholders;
public DynamicExpression(Expression expression, PlaceholderBridge placeholders) {
this.expression = expression;
this.placeholders = placeholders;
}
public double evaluate(String source, Player player, ItemStack item, Map<String, Double> variables) {
return evaluate(source, player, item, variables, false);
}
public double evaluateRoman(String source, Player player, ItemStack item, Map<String, Double> variables) {
return evaluate(source, player, item, variables, true);
}
private double evaluate(String source, Player player, ItemStack item, Map<String, Double> variables, boolean roman) {
String resolved = placeholders.resolve(player, source == null ? "0" : source);
Matcher matcher = ITEM_VARIABLE.matcher(resolved);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
double fallback = parseNumber(matcher.group(3), 0);
double value = resolveItem(matcher.group(1), matcher.group(2), item, fallback, roman);
matcher.appendReplacement(buffer, Matcher.quoteReplacement(Double.toString(value)));
}
matcher.appendTail(buffer);
Map<String, Double> normalized = new HashMap<>();
variables.forEach((key, value) -> {
normalized.put(key, value);
normalized.put(key.toLowerCase(Locale.ROOT), value);
});
return expression.evaluate(buffer.toString(), normalized);
}
public boolean test(String source, Player player, ItemStack item, Map<String, Double> variables) {
return Math.abs(evaluate(source, player, item, variables)) > 1.0E-9;
}
private double resolveItem(String type, String key, ItemStack item, double fallback, boolean roman) {
if (item == null || item.getType().isAir()) return fallback;
return switch (type.toUpperCase(Locale.ROOT)) {
case "LORE" -> lore(item, key, fallback, roman);
case "WORD" -> word(item, key, fallback);
case "NBT" -> nbt(item, key, fallback);
case "ENCHANT" -> enchant(item, key, fallback);
default -> fallback;
};
}
private double word(ItemStack item, String expected, double value) {
if (!item.hasItemMeta()) return 0;
String cleanExpected = strip(expected);
if (item.getItemMeta().hasDisplayName() && strip(item.getItemMeta().getDisplayName()).contains(cleanExpected)) return value;
if (item.getItemMeta().hasLore() && item.getItemMeta().getLore() != null) {
for (String line : item.getItemMeta().getLore()) if (strip(line).contains(cleanExpected)) return value;
}
return 0;
}
private double lore(ItemStack item, String marker, double fallback, boolean roman) {
if (!item.hasItemMeta() || !item.getItemMeta().hasLore() || item.getItemMeta().getLore() == null) return fallback;
String cleanMarker = strip(marker);
for (String line : item.getItemMeta().getLore()) {
String clean = strip(line);
int index = clean.indexOf(cleanMarker);
if (index < 0) continue;
Matcher number = NUMBER.matcher(clean.substring(index + cleanMarker.length()));
if (number.find()) return parseNumber(number.group(), fallback);
if (roman) {
Matcher numeral = Pattern.compile("(?i)\\b[IVXLCDM]+\\b")
.matcher(clean.substring(index + cleanMarker.length()));
if (numeral.find()) return parseRoman(numeral.group(), fallback);
}
}
return fallback;
}
private double nbt(ItemStack item, String key, double fallback) {
try {
return NBT.get(item, nbt -> {
if (!nbt.hasTag(key)) return fallback;
return switch (nbt.getType(key)) {
case NBTTagByte -> nbt.getByte(key).doubleValue();
case NBTTagShort -> nbt.getShort(key).doubleValue();
case NBTTagInt -> nbt.getInteger(key).doubleValue();
case NBTTagLong -> nbt.getLong(key).doubleValue();
case NBTTagFloat -> nbt.getFloat(key).doubleValue();
case NBTTagDouble -> nbt.getDouble(key);
default -> parseNumber(nbt.getString(key), fallback);
};
});
} catch (RuntimeException exception) {
return fallback;
}
}
private double enchant(ItemStack item, String key, double fallback) {
Enchantment enchantment = EnchantmentResolver.resolve(key);
return enchantment == null ? fallback : item.getEnchantmentLevel(enchantment);
}
private String strip(String value) {
return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', value == null ? "" : value));
}
private double parseNumber(String value, double fallback) {
try { return value == null || value.isBlank() ? fallback : Double.parseDouble(value); }
catch (NumberFormatException ignored) { return fallback; }
}
private double parseRoman(String value, double fallback) {
if (value == null || value.isBlank()) return fallback;
int total = 0;
int previous = 0;
String normalized = value.toUpperCase(Locale.ROOT);
for (int index = normalized.length() - 1; index >= 0; index--) {
int current = switch (normalized.charAt(index)) {
case 'I' -> 1; case 'V' -> 5; case 'X' -> 10; case 'L' -> 50;
case 'C' -> 100; case 'D' -> 500; case 'M' -> 1000; default -> 0;
};
if (current == 0) return fallback;
total += current < previous ? -current : current;
previous = current;
}
return total;
}
}