mirror of
https://github.com/funkemunky/KauriV3.git
synced 2026-07-01 18:28:25 +00:00
434 lines
15 KiB
Java
434 lines
15 KiB
Java
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2022 Crypto Morin
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
package dev.brighten.ac.utils;
|
|
|
|
import com.google.common.base.Strings;
|
|
import org.apache.commons.lang.StringUtils;
|
|
import org.apache.commons.lang.Validate;
|
|
import org.apache.commons.lang.WordUtils;
|
|
import org.apache.commons.lang.math.NumberUtils;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
import org.bukkit.potion.PotionType;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
import java.util.*;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
|
|
/**
|
|
* Potion type support for multiple aliases.
|
|
* Uses EssentialsX potion list for aliases.
|
|
* <p>
|
|
* Duration: The duration of the effect in ticks. Values 0 or lower are treated as 1. Optional, and defaults to 1 tick.
|
|
* Amplifier: The amplifier of the effect, with level I having value 0. Optional, and defaults to level I.
|
|
* <p>
|
|
* EssentialsX Potions: https://github.com/EssentialsX/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/Potions.java
|
|
* Status Effect: https://minecraft.gamepedia.com/Status_effect
|
|
* Potions: https://minecraft.gamepedia.com/Potion
|
|
*
|
|
* @author Crypto Morin
|
|
* @version 3.1.0
|
|
* @see PotionEffect
|
|
* @see PotionEffectType
|
|
* @see PotionType
|
|
*/
|
|
public enum XPotion {
|
|
ABSORPTION("ABSORB"),
|
|
BAD_OMEN("OMEN_BAD", "PILLAGER"),
|
|
BLINDNESS("BLIND"),
|
|
CONDUIT_POWER("CONDUIT", "POWER_CONDUIT"),
|
|
CONFUSION("NAUSEA", "SICKNESS", "SICK"),
|
|
DAMAGE_RESISTANCE("RESISTANCE", "ARMOR", "DMG_RESIST", "DMG_RESISTANCE"),
|
|
DARKNESS,
|
|
DOLPHINS_GRACE("DOLPHIN", "GRACE"),
|
|
FAST_DIGGING("HASTE", "SUPER_PICK", "DIGFAST", "DIG_SPEED", "QUICK_MINE", "SHARP"),
|
|
FIRE_RESISTANCE("FIRE_RESIST", "RESIST_FIRE", "FIRE_RESISTANCE"),
|
|
GLOWING("GLOW", "SHINE", "SHINY"),
|
|
HARM("INJURE", "DAMAGE", "HARMING", "INFLICT", "INSTANT_DAMAGE"),
|
|
HEAL("HEALTH", "INSTA_HEAL", "INSTANT_HEAL", "INSTA_HEALTH", "INSTANT_HEALTH"),
|
|
HEALTH_BOOST("BOOST_HEALTH", "BOOST", "HP"),
|
|
HERO_OF_THE_VILLAGE("HERO", "VILLAGE_HERO"),
|
|
HUNGER("STARVE", "HUNGRY"),
|
|
INCREASE_DAMAGE("STRENGTH", "BULL", "STRONG", "ATTACK"),
|
|
INVISIBILITY("INVISIBLE", "VANISH", "INVIS", "DISAPPEAR", "HIDE"),
|
|
JUMP("LEAP", "JUMP_BOOST"),
|
|
LEVITATION("LEVITATE"),
|
|
LUCK("LUCKY"),
|
|
NIGHT_VISION("VISION", "VISION_NIGHT"),
|
|
POISON("VENOM"),
|
|
REGENERATION("REGEN"),
|
|
SATURATION("FOOD"),
|
|
SLOW("SLOWNESS", "SLUGGISH"),
|
|
SLOW_DIGGING("FATIGUE", "DULL", "DIGGING", "SLOW_DIG", "DIG_SLOW"),
|
|
SLOW_FALLING("SLOW_FALL", "FALL_SLOW"),
|
|
SPEED("SPRINT", "RUNFAST", "SWIFT", "FAST"),
|
|
UNLUCK("UNLUCKY"),
|
|
WATER_BREATHING("WATER_BREATH", "UNDERWATER_BREATHING", "UNDERWATER_BREATH", "AIR"),
|
|
WEAKNESS("WEAK"),
|
|
WITHER("DECAY");
|
|
|
|
/**
|
|
* Cached list of {@link XPotion#values()} to avoid allocating memory for
|
|
* calling the method every time.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public static final XPotion[] VALUES = values();
|
|
|
|
/**
|
|
* An unmodifiable set of "bad" potion effects.
|
|
*
|
|
* @since 1.1.0
|
|
*/
|
|
public static final Set<XPotion> DEBUFFS = Collections.unmodifiableSet(EnumSet.of(
|
|
BAD_OMEN, BLINDNESS, CONFUSION, HARM, HUNGER, LEVITATION, POISON,
|
|
SLOW, SLOW_DIGGING, UNLUCK, WEAKNESS, WITHER)
|
|
);
|
|
|
|
/**
|
|
* Efficient mapping to get {@link XPotion} from a {@link PotionEffectType}
|
|
* Note that <code>values.length + 1</code> is intentional as it allocates one useless space since IDs start from 1
|
|
*/
|
|
private static final XPotion[] POTIONEFFECTTYPE_MAPPING = new XPotion[VALUES.length + 1];
|
|
|
|
static {
|
|
for (XPotion pot : VALUES)
|
|
if (pot.type != null) //noinspection deprecation
|
|
POTIONEFFECTTYPE_MAPPING[pot.type.getId()] = pot;
|
|
}
|
|
|
|
private final PotionEffectType type;
|
|
|
|
XPotion(@Nonnull String... aliases) {
|
|
this.type = PotionEffectType.getByName(this.name());
|
|
Data.NAMES.put(this.name(), this);
|
|
for (String legacy : aliases) Data.NAMES.put(legacy, this);
|
|
}
|
|
|
|
/**
|
|
* Attempts to build the string like an enum name.<br>
|
|
* Removes all the spaces, numbers and extra non-English characters. Also removes some config/in-game based strings.
|
|
* While this method is hard to maintain, it's extremely efficient. It's approximately more than x5 times faster than
|
|
* the normal RegEx + String Methods approach for both formatted and unformatted material names.
|
|
*
|
|
* @param name the potion effect type name to format.
|
|
*
|
|
* @return an enum name.
|
|
* @since 1.0.0
|
|
*/
|
|
@Nonnull
|
|
private static String format(@Nonnull String name) {
|
|
int len = name.length();
|
|
char[] chs = new char[len];
|
|
int count = 0;
|
|
boolean appendUnderline = false;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
char ch = name.charAt(i);
|
|
|
|
if (!appendUnderline && count != 0 && (ch == '-' || ch == ' ' || ch == '_') && chs[count] != '_') appendUnderline = true;
|
|
else {
|
|
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
|
|
if (appendUnderline) {
|
|
chs[count++] = '_';
|
|
appendUnderline = false;
|
|
}
|
|
chs[count++] = (char) (ch & 0x5f);
|
|
}
|
|
}
|
|
}
|
|
|
|
return new String(chs, 0, count);
|
|
}
|
|
|
|
/**
|
|
* Parses a potion effect type from the given string.
|
|
* Supports type IDs.
|
|
*
|
|
* @param potion the type of the type's ID of the potion effect type.
|
|
*
|
|
* @return a potion effect type.
|
|
* @since 1.0.0
|
|
*/
|
|
@Nonnull
|
|
public static Optional<XPotion> matchXPotion(@Nonnull String potion) {
|
|
Validate.notEmpty(potion, "Cannot match XPotion of a null or empty potion effect type");
|
|
PotionEffectType idType = fromId(potion);
|
|
if (idType != null) {
|
|
XPotion type = Data.NAMES.get(idType.getName());
|
|
if (type == null) throw new NullPointerException("Unsupported potion effect type ID: " + idType);
|
|
return Optional.of(type);
|
|
}
|
|
return Optional.ofNullable(Data.NAMES.get(format(potion)));
|
|
}
|
|
|
|
/**
|
|
* Parses the XPotion for this potion effect.
|
|
*
|
|
* @param type the potion effect type.
|
|
*
|
|
* @return the XPotion of this potion effect.
|
|
* @throws IllegalArgumentException may be thrown as an unexpected exception.
|
|
* @since 1.0.0
|
|
*/
|
|
@SuppressWarnings("deprecation")
|
|
@Nonnull
|
|
public static XPotion matchXPotion(@Nonnull PotionEffectType type) {
|
|
Objects.requireNonNull(type, "Cannot match XPotion of a null potion effect type");
|
|
return POTIONEFFECTTYPE_MAPPING[type.getId()];
|
|
}
|
|
|
|
/**
|
|
* Parses the type ID if available.
|
|
*
|
|
* @param type the ID of the potion effect type.
|
|
*
|
|
* @return a potion effect type from the ID, or null if it's not an ID or the effect is not found.
|
|
* @since 1.0.0
|
|
*/
|
|
@Nullable
|
|
@SuppressWarnings("deprecation")
|
|
private static PotionEffectType fromId(@Nonnull String type) {
|
|
try {
|
|
int id = Integer.parseInt(type);
|
|
return PotionEffectType.getById(id);
|
|
} catch (NumberFormatException ex) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a {@link PotionEffect} from a string, usually from config.
|
|
* Supports potion type IDs.
|
|
* <br>
|
|
* Format: <b>Potion, Duration (in seconds), Amplifier (level) [%chance]</b>
|
|
* <pre>
|
|
* WEAKNESS, 30, 1
|
|
* SLOWNESS 200 10
|
|
* 1, 10000, 100 %50
|
|
* </pre>
|
|
* The last argument (the amplifier can also have a chance which if not met, returns null.
|
|
*
|
|
* @param potion the potion string to parse.
|
|
*
|
|
* @return a potion effect, or null if the potion type is wrong.
|
|
* @see #buildPotionEffect(int, int)
|
|
* @since 1.0.0
|
|
*/
|
|
@Nullable
|
|
public static Effect parseEffect(@Nullable String potion) {
|
|
if (Strings.isNullOrEmpty(potion) || potion.equalsIgnoreCase("none")) return null;
|
|
String[] split = StringUtils.split(StringUtils.deleteWhitespace(potion), ',');
|
|
if (split.length == 0) split = StringUtils.split(potion, ' ');
|
|
|
|
double chance = 100;
|
|
int chanceIndex = 0;
|
|
if (split.length > 2) {
|
|
chanceIndex = split[2].indexOf('%');
|
|
if (chanceIndex != -1) chance = NumberUtils.toDouble(split[2].substring(chanceIndex + 1), 100);
|
|
}
|
|
|
|
Optional<XPotion> typeOpt = matchXPotion(split[0]);
|
|
if (!typeOpt.isPresent()) return null;
|
|
PotionEffectType type = typeOpt.get().type;
|
|
if (type == null) return null;
|
|
|
|
int duration = 2400; // 20 ticks * 60 seconds * 2 minutes
|
|
int amplifier = 0;
|
|
if (split.length > 1) {
|
|
duration = NumberUtils.toInt(split[1]) * 20;
|
|
if (split.length > 2) amplifier = NumberUtils.toInt(chanceIndex <= 0 ? split[2] : split[2].substring(0, chanceIndex)) - 1;
|
|
}
|
|
|
|
return new Effect(new PotionEffect(type, duration, amplifier), chance);
|
|
}
|
|
|
|
/**
|
|
* Add a list of potion effects to an entity from a string list, usually from config.
|
|
*
|
|
* @param entity the entity to add potion effects to.
|
|
* @param effects the list of potion effects to parse and add to the entity.
|
|
*
|
|
* @see #parseEffect(String)
|
|
* @since 1.0.0
|
|
*/
|
|
public static void addEffects(@Nonnull LivingEntity entity, @Nullable List<String> effects) {
|
|
Objects.requireNonNull(entity, "Cannot add potion effects to null entity");
|
|
for (Effect effect : parseEffects(effects)) effect.apply(entity);
|
|
}
|
|
|
|
/**
|
|
* @param effectsString a list of effects with a format following {@link #parseEffect(String)}
|
|
*
|
|
* @return a list of parsed effets.
|
|
* @since 3.0.0
|
|
*/
|
|
public static List<Effect> parseEffects(@Nullable List<String> effectsString) {
|
|
if (effectsString == null || effectsString.isEmpty()) return new ArrayList<>();
|
|
List<Effect> effects = new ArrayList<>(effectsString.size());
|
|
|
|
for (String effectStr : effectsString) {
|
|
Effect effect = parseEffect(effectStr);
|
|
if (effect != null) effects.add(effect);
|
|
}
|
|
|
|
return effects;
|
|
}
|
|
|
|
/**
|
|
* Checks if a material can have potion effects.
|
|
* This method does not check for {@code LEGACY} materials.
|
|
* You should avoid using them or use XMaterial instead.
|
|
*
|
|
* @param material the material to check.
|
|
*
|
|
* @return true if the material is a potion, otherwise false.
|
|
* @since 1.0.0
|
|
*/
|
|
public static boolean canHaveEffects(@Nullable Material material) {
|
|
return material != null && (material.name().endsWith("POTION") || material.name().startsWith("TIPPED_ARROW"));
|
|
}
|
|
|
|
/**
|
|
* Parses the potion effect type.
|
|
*
|
|
* @return the parsed potion effect type.
|
|
* @see #getPotionType()
|
|
* @since 1.0.0
|
|
*/
|
|
@Nullable
|
|
public PotionEffectType getPotionEffectType() {
|
|
return this.type;
|
|
}
|
|
|
|
/**
|
|
* Checks if this potion is supported in the current Minecraft version.
|
|
* <p>
|
|
* An invocation of this method yields exactly the same result as the expression:
|
|
* <p>
|
|
* <blockquote>
|
|
* {@link #getPotionEffectType()} != null
|
|
* </blockquote>
|
|
*
|
|
* @return true if the current version has this potion effect type, otherwise false.
|
|
* @since 1.0.0
|
|
*/
|
|
public boolean isSupported() {
|
|
return this.type != null;
|
|
}
|
|
|
|
/**
|
|
* Gets the PotionType from this PotionEffectType.
|
|
* Usually for potion items.
|
|
*
|
|
* @return a potion type for potions.
|
|
* @see #getPotionEffectType()
|
|
* @since 1.0.0
|
|
* @deprecated not for removal, but use {@link PotionEffectType} instead.
|
|
*/
|
|
@Nullable
|
|
@Deprecated
|
|
public PotionType getPotionType() {
|
|
return type == null ? null : PotionType.getByEffect(type);
|
|
}
|
|
|
|
/**
|
|
* Builds a potion effect with the given duration and amplifier.
|
|
*
|
|
* @param duration the duration of the potion effect.
|
|
* @param amplifier the amplifier of the potion effect.
|
|
*
|
|
* @return a potion effect.
|
|
* @see #parseEffect(String)
|
|
* @since 1.0.0
|
|
*/
|
|
@Nullable
|
|
public PotionEffect buildPotionEffect(int duration, int amplifier) {
|
|
return type == null ? null : new PotionEffect(type, duration, amplifier);
|
|
}
|
|
|
|
/**
|
|
* In most cases you should be using {@link #name()} instead.
|
|
*
|
|
* @return a friendly readable string name.
|
|
*/
|
|
@Override
|
|
public String toString() {
|
|
return WordUtils.capitalize(this.name().replace('_', ' ').toLowerCase(Locale.ENGLISH));
|
|
}
|
|
|
|
/**
|
|
* Used for data that need to be accessed during enum initialization.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
private static final class Data {
|
|
private static final Map<String, XPotion> NAMES = new HashMap<>();
|
|
}
|
|
|
|
/**
|
|
* For now, this merely acts as a chance wrapper for potion effects.
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
public static class Effect {
|
|
private PotionEffect effect;
|
|
private double chance;
|
|
|
|
public Effect(PotionEffect effect, double chance) {
|
|
this.effect = effect;
|
|
this.chance = chance;
|
|
}
|
|
|
|
public XPotion getXPotion() {
|
|
return XPotion.matchXPotion(effect.getType());
|
|
}
|
|
|
|
public double getChance() {
|
|
return chance;
|
|
}
|
|
|
|
public boolean hasChance() {
|
|
return chance >= 100 || ThreadLocalRandom.current().nextDouble(0, 100) <= chance;
|
|
}
|
|
|
|
public void setChance(double chance) {
|
|
this.chance = chance;
|
|
}
|
|
|
|
public void apply(LivingEntity entity) {
|
|
if (hasChance()) entity.addPotionEffect(effect);
|
|
}
|
|
|
|
public PotionEffect getEffect() {
|
|
return effect;
|
|
}
|
|
|
|
public void setEffect(PotionEffect effect) {
|
|
this.effect = effect;
|
|
}
|
|
}
|
|
}
|