All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bukkit.potion.Potion Maven / Gradle / Ivy

package org.bukkit.potion;

import com.google.common.collect.ImmutableList;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;

import java.util.Collection;

/**
 * Represents a minecraft potion
 */
public class Potion {
  private static final int EXTENDED_BIT = 0x40;
  private static final int POTION_BIT = 0xF;
  private static final int SPLASH_BIT = 0x4000;
  private static final int TIER_BIT = 0x20;
  private static final int TIER_SHIFT = 5;
  private static final int NAME_BIT = 0x3F;
  private static PotionBrewer brewer;
  private boolean extended = false;
  private boolean splash = false;
  private int level = 1;
  private int name = -1;
  private PotionType type;

  /**
   * Construct a new potion of the given type. Unless the type is {@link
   * PotionType#WATER}, it will be level one, without extended duration.
   * Don't use this constructor to create a no-effect potion other than
   * water bottle.
   *
   * @param type The potion type
   * @see #Potion(int)
   */
  public Potion(PotionType type) {
    this.type = type;
    if (type != null) {
      this.name = type.getDamageValue();
    }
    if (type == null || type == PotionType.WATER) {
      this.level = 0;
    }
  }

  /**
   * @param type the type of the potion
   * @param tier the tier of the potion
   * @deprecated In favour of {@link #Potion(PotionType, int)}
   */
  @Deprecated
  public Potion(PotionType type, Tier tier) {
    this(type, tier == Tier.TWO ? 2 : 1);
    Validate.notNull(type, "Type cannot be null");
  }

  /**
   * @param type   the type of the potion
   * @param tier   the tier of the potion
   * @param splash whether the potion is a splash potion
   * @deprecated In favour of {@link #Potion(PotionType, int, boolean)}
   */
  @Deprecated
  public Potion(PotionType type, Tier tier, boolean splash) {
    this(type, tier == Tier.TWO ? 2 : 1, splash);
  }

  /**
   * @param type     the type of the potion
   * @param tier     the tier of the potion
   * @param splash   whether the potion is a splash potion
   * @param extended whether the potion has an extended duration
   * @deprecated In favour of {@link #Potion(PotionType, int, boolean,
   * boolean)}
   */
  @Deprecated
  public Potion(PotionType type, Tier tier, boolean splash, boolean extended) {
    this(type, tier, splash);
    this.extended = extended;
  }

  /**
   * Create a new potion of the given type and level.
   *
   * @param type  The type of potion.
   * @param level The potion's level.
   */
  public Potion(PotionType type, int level) {
    this(type);
    Validate.notNull(type, "Type cannot be null");
    Validate.isTrue(type != PotionType.WATER, "Water bottles don't have a level!");
    Validate.isTrue(level > 0 && level < 3, "Level must be 1 or 2");
    this.level = level;
  }

  /**
   * Create a new potion of the given type and level.
   *
   * @param type   The type of potion.
   * @param level  The potion's level.
   * @param splash Whether it is a splash potion.
   * @deprecated In favour of using {@link #Potion(PotionType)} with {@link
   * #splash()}.
   */
  @Deprecated
  public Potion(PotionType type, int level, boolean splash) {
    this(type, level);
    this.splash = splash;
  }

  /**
   * Create a new potion of the given type and level.
   *
   * @param type     The type of potion.
   * @param level    The potion's level.
   * @param splash   Whether it is a splash potion.
   * @param extended Whether it has an extended duration.
   * @deprecated In favour of using {@link #Potion(PotionType)} with {@link
   * #extend()} and possibly {@link #splash()}.
   */
  @Deprecated
  public Potion(PotionType type, int level, boolean splash, boolean extended) {
    this(type, level, splash);
    this.extended = extended;
  }

  /**
   * Create a potion with a specific name.
   *
   * @param name The name index (0-63)
   */
  public Potion(int name) {
    this(PotionType.getByDamageValue(name & POTION_BIT));
    this.name = name & NAME_BIT;
    if ((name & POTION_BIT) == 0) {
      // If it's 0 it would've become PotionType.WATER, but it should actually be mundane potion
      this.type = null;
    }
  }

  /**
   * @param damage the damage value
   * @return the produced potion
   * @deprecated Magic value
   */
  @Deprecated
  public static Potion fromDamage(int damage) {
    PotionType type = PotionType.getByDamageValue(damage & POTION_BIT);
    Potion potion;
    if (type == null || type == PotionType.WATER) {
      potion = new Potion(damage & NAME_BIT);
    } else {
      int level = (damage & TIER_BIT) >> TIER_SHIFT;
      level++;
      potion = new Potion(type, level);
    }
    if ((damage & SPLASH_BIT) > 0) {
      potion = potion.splash();
    }
    if ((type == null || !type.isInstant()) && (damage & EXTENDED_BIT) > 0) {
      potion = potion.extend();
    }
    return potion;
  }

  public static Potion fromItemStack(ItemStack item) {
    Validate.notNull(item, "item cannot be null");
    if (item.getType() != Material.POTION)
      throw new IllegalArgumentException("item is not a potion");
    return fromDamage(item.getDurability());
  }

  /**
   * Returns an instance of {@link PotionBrewer}.
   *
   * @return An instance of PotionBrewer
   */
  public static PotionBrewer getBrewer() {
    return brewer;
  }

  /**
   * Sets the current instance of {@link PotionBrewer}. Generally not to be
   * used from within a plugin.
   *
   * @param other The new PotionBrewer
   */
  public static void setPotionBrewer(PotionBrewer other) {
    if (brewer != null)
      throw new IllegalArgumentException("brewer can only be set internally");
    brewer = other;
  }

  /**
   * Chain this to the constructor to make the potion a splash potion.
   *
   * @return The potion.
   */
  public Potion splash() {
    setSplash(true);
    return this;
  }

  /**
   * Chain this to the constructor to extend the potion's duration.
   *
   * @return The potion.
   */
  public Potion extend() {
    setHasExtendedDuration(true);
    return this;
  }

  /**
   * Applies the effects of this potion to the given {@link ItemStack}. The
   * ItemStack must be a potion.
   *
   * @param to The itemstack to apply to
   */
  public void apply(ItemStack to) {
    Validate.notNull(to, "itemstack cannot be null");
    Validate.isTrue(to.getType() == Material.POTION, "given itemstack is not a potion");
    to.setDurability(toDamageValue());
  }

  /**
   * Applies the effects that would be applied by this potion to the given
   * {@link LivingEntity}.
   *
   * @param to The entity to apply the effects to
   * @see LivingEntity#addPotionEffects(Collection)
   */
  public void apply(LivingEntity to) {
    Validate.notNull(to, "entity cannot be null");
    to.addPotionEffects(getEffects());
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    Potion other = (Potion) obj;
    return extended == other.extended && splash == other.splash && level == other.level && type == other.type;
  }

  /**
   * Returns a collection of {@link PotionEffect}s that this {@link Potion}
   * would confer upon a {@link LivingEntity}.
   *
   * @return The effects that this potion applies
   * @see PotionBrewer#getEffectsFromDamage(int)
   * @see Potion#toDamageValue()
   */
  public Collection getEffects() {
    if (type == null) return ImmutableList.of();
    return getBrewer().getEffectsFromDamage(toDamageValue());
  }

  /**
   * Returns the level of this potion.
   *
   * @return The level of this potion
   */
  public int getLevel() {
    return level;
  }

  /**
   * Sets the level of this potion.
   *
   * @param level The new level of this potion
   */
  public void setLevel(int level) {
    Validate.notNull(this.type, "No-effect potions don't have a level.");
    int max = type.getMaxLevel();
    Validate.isTrue(level > 0 && level <= max, "Level must be " + (max == 1 ? "" : "between 1 and ") + max + " for this potion");
    this.level = level;
  }

  /**
   * Returns the {@link Tier} of this potion.
   *
   * @return The tier of this potion
   */
  @Deprecated
  public Tier getTier() {
    return level == 2 ? Tier.TWO : Tier.ONE;
  }

  /**
   * Sets the {@link Tier} of this potion.
   *
   * @param tier The new tier of this potion
   * @deprecated In favour of {@link #setLevel(int)}
   */
  @Deprecated
  public void setTier(Tier tier) {
    Validate.notNull(tier, "tier cannot be null");
    this.level = (tier == Tier.TWO ? 2 : 1);
  }

  /**
   * Returns the {@link PotionType} of this potion.
   *
   * @return The type of this potion
   */
  public PotionType getType() {
    return type;
  }

  /**
   * Sets the {@link PotionType} of this potion.
   *
   * @param type The new type of this potion
   */
  public void setType(PotionType type) {
    this.type = type;
  }

  /**
   * Returns whether this potion has an extended duration.
   *
   * @return Whether this potion has extended duration
   */
  public boolean hasExtendedDuration() {
    return extended;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = prime + level;
    result = prime * result + (extended ? 1231 : 1237);
    result = prime * result + (splash ? 1231 : 1237);
    result = prime * result + ((type == null) ? 0 : type.hashCode());
    return result;
  }

  /**
   * Returns whether this potion is a splash potion.
   *
   * @return Whether this is a splash potion
   */
  public boolean isSplash() {
    return splash;
  }

  /**
   * Sets whether this potion is a splash potion. Splash potions can be
   * thrown for a radius effect.
   *
   * @param isSplash Whether this is a splash potion
   */
  public void setSplash(boolean isSplash) {
    splash = isSplash;
  }

  /**
   * Set whether this potion has extended duration. This will cause the
   * potion to have roughly 8/3 more duration than a regular potion.
   *
   * @param isExtended Whether the potion should have extended duration
   */
  public void setHasExtendedDuration(boolean isExtended) {
    Validate.isTrue(type == null || !type.isInstant(), "Instant potions cannot be extended");
    extended = isExtended;
  }

  /**
   * Converts this potion to a valid potion damage short, usable for potion
   * item stacks.
   *
   * @return The damage value of this potion
   * @deprecated Magic value
   */
  @Deprecated
  public short toDamageValue() {
    short damage;
    if (type == PotionType.WATER) {
      return 0;
    } else if (type == null) {
      // Without this, mundanePotion.toDamageValue() would return 0
      damage = (short) (name == 0 ? 8192 : name);
    } else {
      damage = (short) (level - 1);
      damage <<= TIER_SHIFT;
      damage |= (short) type.getDamageValue();
    }
    if (splash) {
      damage |= SPLASH_BIT;
    }
    if (extended) {
      damage |= EXTENDED_BIT;
    }
    return damage;
  }

  /**
   * Converts this potion to an {@link ItemStack} with the specified amount
   * and a correct damage value.
   *
   * @param amount The amount of the ItemStack
   * @return The created ItemStack
   */
  public ItemStack toItemStack(int amount) {
    return new ItemStack(Material.POTION, amount, toDamageValue());
  }

  /**
   * @return the name id
   * @deprecated Magic value
   */
  @Deprecated
  public int getNameId() {
    return name;
  }

  @Deprecated
  public enum Tier {
    ONE(0),
    TWO(0x20);

    private final int damageBit;

    Tier(int bit) {
      damageBit = bit;
    }

    public static Tier getByDamageBit(int damageBit) {
      for (Tier tier : Tier.values()) {
        if (tier.damageBit == damageBit)
          return tier;
      }
      return null;
    }

    public int getDamageBit() {
      return damageBit;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy