package tc.oc.commons.bukkit.util;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.bukkit.entity.AreaEffectCloud;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.entity.TippedArrow;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionBrew;
import org.bukkit.potion.PotionData;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import static tc.oc.commons.bukkit.util.PotionUtils.effects;
/**
* Classification for potions that represents their harmfulness (or lack thereof). Not explicitly limited to {@link
* org.bukkit.entity.ThrownPotion}s; other logical use cases include representation of {@link
* org.bukkit.potion.PotionEffect}s and {@link org.bukkit.potion.PotionEffectType}s.
*/
public enum PotionClassification {
/** Beneficial, has positive implications */
BENEFICIAL(1),
/** Unknown, positiveness of implications is not determinable */
UNKNOWN(0),
/** Harmful, has negative implications */
HARMFUL(-1);
private final int score;
/** @param score Score of implications' positiveness */
PotionClassification(final int score) {
this.score = score;
}
/** An integer representation of this classification */
public int getScore() {
return this.score;
}
public PotionClassification inverse() {
switch(this) {
case BENEFICIAL: return HARMFUL;
case HARMFUL: return BENEFICIAL;
default: return this;
}
}
private static final ImmutableSet<PotionEffectType> BUFFS = ImmutableSet.<PotionEffectType>builder()
.add(PotionEffectType.ABSORPTION)
.add(PotionEffectType.DAMAGE_RESISTANCE)
.add(PotionEffectType.FAST_DIGGING)
.add(PotionEffectType.FIRE_RESISTANCE)
.add(PotionEffectType.HEAL)
.add(PotionEffectType.HEALTH_BOOST)
.add(PotionEffectType.INCREASE_DAMAGE)
.add(PotionEffectType.INVISIBILITY)
.add(PotionEffectType.JUMP)
.add(PotionEffectType.LUCK)
.add(PotionEffectType.NIGHT_VISION)
.add(PotionEffectType.REGENERATION)
.add(PotionEffectType.SATURATION)
.add(PotionEffectType.SPEED)
.add(PotionEffectType.WATER_BREATHING)
.build();
private static final ImmutableSet<PotionEffectType> BANES = ImmutableSet.<PotionEffectType>builder()
.add(PotionEffectType.BLINDNESS)
.add(PotionEffectType.CONFUSION)
.add(PotionEffectType.HARM)
.add(PotionEffectType.HUNGER)
.add(PotionEffectType.POISON)
.add(PotionEffectType.LEVITATION)
.add(PotionEffectType.SLOW)
.add(PotionEffectType.SLOW_DIGGING)
.add(PotionEffectType.UNLUCK)
.add(PotionEffectType.WEAKNESS)
.add(PotionEffectType.WITHER)
.build();
private static final ImmutableMap<PotionEffectType, PotionClassification> CLASSIFICATIONS;
static {
final ImmutableMap.Builder<PotionEffectType, PotionClassification> builder = ImmutableMap.builder();
for(PotionEffectType buff : PotionClassification.BUFFS) builder.put(buff, PotionClassification.BENEFICIAL);
for(PotionEffectType buff : PotionClassification.BANES) builder.put(buff, PotionClassification.HARMFUL);
CLASSIFICATIONS = builder.build();
}
/**
* Scores the specified {@link PotionEffect}.
*
* @param effect The effect to score.
* @return The score.
*/
private static double getScore(final PotionEffect effect) throws IllegalArgumentException {
return classify(effect.getType(), effect.getAmplifier()).getScore()
* (effect.getAmplifier() < 0 ? -effect.getAmplifier() : effect.getAmplifier() + 1)
* ((double) effect.getDuration()) / 20d;
}
/**
* Scores the specified {@link PotionEffect}s.
*
* @param effects The potion effects to score.
* @return The score.
*/
private static double getScore(final Iterable<PotionEffect> effects) {
double score = 0;
for (PotionEffect effect : effects) {
score += getScore(effect);
}
return score;
}
/**
* Gets a classification for the given score. {@link #BENEFICIAL} if > 0, {@link #UNKNOWN} if == 0, {@link
* #HARMFUL} if < 0.
*/
private static PotionClassification fromScore(final double score) {
return (score > 0.0 ? BENEFICIAL : (score == 0.0 ? UNKNOWN : HARMFUL));
}
public static PotionClassification classify(PotionEffectType type) {
final PotionClassification c = CLASSIFICATIONS.get(type);
return c == null ? UNKNOWN : c;
}
public static PotionClassification classify(PotionEffectType type, int level) {
final PotionClassification c = classify(type);
return level < 0 ? c.inverse() : c;
}
/**
* Classifies the provided {@link PotionEffect}s as either {@link PotionClassification#BENEFICIAL}, {@link
* PotionClassification#UNKNOWN}, or {@link PotionClassification#HARMFUL}.
*/
public static PotionClassification classify(final Iterable<PotionEffect> effects) {
return fromScore(getScore(effects));
}
public static PotionClassification classify(PotionData potion) {
return classify(effects(potion));
}
public static PotionClassification classify(PotionBrew potion) {
return classify(potion.effects());
}
public static PotionClassification classify(PotionMeta potion) {
return classify(effects(potion));
}
public static PotionClassification classify(ItemStack potion) {
return classify(effects(potion));
}
public static PotionClassification classify(final ThrownPotion potion) {
return classify(potion.getEffects());
}
public static PotionClassification classify(AreaEffectCloud cloud) {
return classify(effects(cloud));
}
public static PotionClassification classify(TippedArrow arrow) {
return classify(effects(arrow));
}
}