001 package org.bukkit.potion; 002 003 import java.util.Collection; 004 005 import org.apache.commons.lang.Validate; 006 import org.bukkit.Material; 007 import org.bukkit.entity.LivingEntity; 008 import org.bukkit.inventory.ItemStack; 009 010 import com.google.common.collect.ImmutableList; 011 012 /** 013 * Represents a minecraft potion 014 */ 015 public class Potion { 016 private boolean extended = false; 017 private boolean splash = false; 018 private int level = 1; 019 private int name = -1; 020 private PotionType type; 021 022 /** 023 * Construct a new potion of the given type. Unless the type is {@link 024 * PotionType#WATER}, it will be level one, without extended duration. 025 * Don't use this constructor to create a no-effect potion other than 026 * water bottle. 027 * 028 * @param type The potion type 029 * @see #Potion(int) 030 */ 031 public Potion(PotionType type) { 032 this.type = type; 033 if (type != null) { 034 this.name = type.getDamageValue(); 035 } 036 if (type == null || type == PotionType.WATER) { 037 this.level = 0; 038 } 039 } 040 041 /** 042 * @deprecated In favour of {@link #Potion(PotionType, int)} 043 */ 044 @SuppressWarnings("javadoc") 045 @Deprecated 046 public Potion(PotionType type, Tier tier) { 047 this(type, tier == Tier.TWO ? 2 : 1); 048 Validate.notNull(type, "Type cannot be null"); 049 } 050 051 /** 052 * @deprecated In favour of {@link #Potion(PotionType, int, boolean)} 053 */ 054 @SuppressWarnings("javadoc") 055 @Deprecated 056 public Potion(PotionType type, Tier tier, boolean splash) { 057 this(type, tier == Tier.TWO ? 2 : 1, splash); 058 } 059 060 /** 061 * @deprecated In favour of {@link #Potion(PotionType, int, boolean, 062 * boolean)} 063 */ 064 @SuppressWarnings("javadoc") 065 @Deprecated 066 public Potion(PotionType type, Tier tier, boolean splash, boolean extended) { 067 this(type, tier, splash); 068 this.extended = extended; 069 } 070 071 /** 072 * Create a new potion of the given type and level. 073 * 074 * @param type The type of potion. 075 * @param level The potion's level. 076 */ 077 public Potion(PotionType type, int level) { 078 this(type); 079 Validate.notNull(type, "Type cannot be null"); 080 Validate.isTrue(type != PotionType.WATER, "Water bottles don't have a level!"); 081 Validate.isTrue(level > 0 && level < 3, "Level must be 1 or 2"); 082 this.level = level; 083 } 084 085 /** 086 * Create a new potion of the given type and level. 087 * 088 * @param type The type of potion. 089 * @param level The potion's level. 090 * @param splash Whether it is a splash potion. 091 * @deprecated In favour of using {@link #Potion(PotionType)} with {@link 092 * #splash()}. 093 */ 094 @Deprecated 095 public Potion(PotionType type, int level, boolean splash) { 096 this(type, level); 097 this.splash = splash; 098 } 099 100 /** 101 * Create a new potion of the given type and level. 102 * 103 * @param type The type of potion. 104 * @param level The potion's level. 105 * @param splash Whether it is a splash potion. 106 * @param extended Whether it has an extended duration. 107 * @deprecated In favour of using {@link #Potion(PotionType)} with {@link 108 * #extend()} and possibly {@link #splash()}. 109 */ 110 @Deprecated 111 public Potion(PotionType type, int level, boolean splash, boolean extended) { 112 this(type, level, splash); 113 this.extended = extended; 114 } 115 116 /** 117 * Create a potion with a specific name. 118 * 119 * @param name The name index (0-63) 120 */ 121 public Potion(int name) { 122 this(PotionType.getByDamageValue(name & POTION_BIT)); 123 this.name = name & NAME_BIT; 124 if ((name & POTION_BIT) == 0) { 125 // If it's 0 it would've become PotionType.WATER, but it should actually be mundane potion 126 this.type = null; 127 } 128 } 129 130 /** 131 * Chain this to the constructor to make the potion a splash potion. 132 * 133 * @return The potion. 134 */ 135 public Potion splash() { 136 setSplash(true); 137 return this; 138 } 139 140 /** 141 * Chain this to the constructor to extend the potion's duration. 142 * 143 * @return The potion. 144 */ 145 public Potion extend() { 146 setHasExtendedDuration(true); 147 return this; 148 } 149 150 /** 151 * Applies the effects of this potion to the given {@link ItemStack}. The 152 * ItemStack must be a potion. 153 * 154 * @param to The itemstack to apply to 155 */ 156 public void apply(ItemStack to) { 157 Validate.notNull(to, "itemstack cannot be null"); 158 Validate.isTrue(to.getType() == Material.POTION, "given itemstack is not a potion"); 159 to.setDurability(toDamageValue()); 160 } 161 162 /** 163 * Applies the effects that would be applied by this potion to the given 164 * {@link LivingEntity}. 165 * 166 * @see LivingEntity#addPotionEffects(Collection) 167 * @param to The entity to apply the effects to 168 */ 169 public void apply(LivingEntity to) { 170 Validate.notNull(to, "entity cannot be null"); 171 to.addPotionEffects(getEffects()); 172 } 173 174 @Override 175 public boolean equals(Object obj) { 176 if (this == obj) { 177 return true; 178 } 179 if (obj == null || getClass() != obj.getClass()) { 180 return false; 181 } 182 Potion other = (Potion) obj; 183 return extended == other.extended && splash == other.splash && level == other.level && type == other.type; 184 } 185 186 /** 187 * Returns a collection of {@link PotionEffect}s that this {@link Potion} 188 * would confer upon a {@link LivingEntity}. 189 * 190 * @see PotionBrewer#getEffectsFromDamage(int) 191 * @see Potion#toDamageValue() 192 * @return The effects that this potion applies 193 */ 194 public Collection<PotionEffect> getEffects() { 195 if (type == null) return ImmutableList.<PotionEffect>of(); 196 return getBrewer().getEffectsFromDamage(toDamageValue()); 197 } 198 199 /** 200 * Returns the level of this potion. 201 * 202 * @return The level of this potion 203 */ 204 public int getLevel() { 205 return level; 206 } 207 208 /** 209 * Returns the {@link Tier} of this potion. 210 * 211 * @return The tier of this potion 212 */ 213 @Deprecated 214 public Tier getTier() { 215 return level == 2 ? Tier.TWO : Tier.ONE; 216 } 217 218 /** 219 * Returns the {@link PotionType} of this potion. 220 * 221 * @return The type of this potion 222 */ 223 public PotionType getType() { 224 return type; 225 } 226 227 /** 228 * Returns whether this potion has an extended duration. 229 * 230 * @return Whether this potion has extended duration 231 */ 232 public boolean hasExtendedDuration() { 233 return extended; 234 } 235 236 @Override 237 public int hashCode() { 238 final int prime = 31; 239 int result = prime + level; 240 result = prime * result + (extended ? 1231 : 1237); 241 result = prime * result + (splash ? 1231 : 1237); 242 result = prime * result + ((type == null) ? 0 : type.hashCode()); 243 return result; 244 } 245 246 /** 247 * Returns whether this potion is a splash potion. 248 * 249 * @return Whether this is a splash potion 250 */ 251 public boolean isSplash() { 252 return splash; 253 } 254 255 /** 256 * Set whether this potion has extended duration. This will cause the 257 * potion to have roughly 8/3 more duration than a regular potion. 258 * 259 * @param isExtended Whether the potion should have extended duration 260 */ 261 public void setHasExtendedDuration(boolean isExtended) { 262 Validate.isTrue(type == null || !type.isInstant(), "Instant potions cannot be extended"); 263 extended = isExtended; 264 } 265 266 /** 267 * Sets whether this potion is a splash potion. Splash potions can be 268 * thrown for a radius effect. 269 * 270 * @param isSplash Whether this is a splash potion 271 */ 272 public void setSplash(boolean isSplash) { 273 splash = isSplash; 274 } 275 276 /** 277 * Sets the {@link Tier} of this potion. 278 * 279 * @param tier The new tier of this potion 280 * @deprecated In favour of {@link #setLevel(int)} 281 */ 282 @Deprecated 283 public void setTier(Tier tier) { 284 Validate.notNull(tier, "tier cannot be null"); 285 this.level = (tier == Tier.TWO ? 2 : 1); 286 } 287 288 /** 289 * Sets the {@link PotionType} of this potion. 290 * 291 * @param type The new type of this potion 292 */ 293 public void setType(PotionType type) { 294 this.type = type; 295 } 296 297 /** 298 * Sets the level of this potion. 299 * 300 * @param level The new level of this potion 301 */ 302 public void setLevel(int level) { 303 Validate.notNull(this.type, "No-effect potions don't have a level."); 304 int max = type.getMaxLevel(); 305 Validate.isTrue(level > 0 && level <= max, "Level must be " + (max == 1 ? "" : "between 1 and ") + max + " for this potion"); 306 this.level = level; 307 } 308 309 /** 310 * Converts this potion to a valid potion damage short, usable for potion 311 * item stacks. 312 * 313 * @return The damage value of this potion 314 * @deprecated Magic value 315 */ 316 @Deprecated 317 public short toDamageValue() { 318 short damage; 319 if (type == PotionType.WATER) { 320 return 0; 321 } else if (type == null) { 322 // Without this, mundanePotion.toDamageValue() would return 0 323 damage = (short) (name == 0 ? 8192 : name); 324 } else { 325 damage = (short) (level - 1); 326 damage <<= TIER_SHIFT; 327 damage |= (short) type.getDamageValue(); 328 } 329 if (splash) { 330 damage |= SPLASH_BIT; 331 } 332 if (extended) { 333 damage |= EXTENDED_BIT; 334 } 335 return damage; 336 } 337 338 /** 339 * Converts this potion to an {@link ItemStack} with the specified amount 340 * and a correct damage value. 341 * 342 * @param amount The amount of the ItemStack 343 * @return The created ItemStack 344 */ 345 public ItemStack toItemStack(int amount) { 346 return new ItemStack(Material.POTION, amount, toDamageValue()); 347 } 348 349 @Deprecated 350 public enum Tier { 351 ONE(0), 352 TWO(0x20); 353 354 private int damageBit; 355 356 Tier(int bit) { 357 damageBit = bit; 358 } 359 360 public int getDamageBit() { 361 return damageBit; 362 } 363 364 public static Tier getByDamageBit(int damageBit) { 365 for (Tier tier : Tier.values()) { 366 if (tier.damageBit == damageBit) 367 return tier; 368 } 369 return null; 370 } 371 } 372 373 private static PotionBrewer brewer; 374 375 private static final int EXTENDED_BIT = 0x40; 376 private static final int POTION_BIT = 0xF; 377 private static final int SPLASH_BIT = 0x4000; 378 private static final int TIER_BIT = 0x20; 379 private static final int TIER_SHIFT = 5; 380 private static final int NAME_BIT = 0x3F; 381 382 /** 383 * 384 * @deprecated Magic value 385 */ 386 @Deprecated 387 public static Potion fromDamage(int damage) { 388 PotionType type = PotionType.getByDamageValue(damage & POTION_BIT); 389 Potion potion; 390 if (type == null || (type == PotionType.WATER && damage != 0)) { 391 potion = new Potion(damage & NAME_BIT); 392 } else { 393 int level = (damage & TIER_BIT) >> TIER_SHIFT; 394 level++; 395 potion = new Potion(type, level); 396 } 397 if ((damage & SPLASH_BIT) > 0) { 398 potion = potion.splash(); 399 } 400 if ((damage & EXTENDED_BIT) > 0) { 401 potion = potion.extend(); 402 } 403 return potion; 404 } 405 406 public static Potion fromItemStack(ItemStack item) { 407 Validate.notNull(item, "item cannot be null"); 408 if (item.getType() != Material.POTION) 409 throw new IllegalArgumentException("item is not a potion"); 410 return fromDamage(item.getDurability()); 411 } 412 413 /** 414 * Returns an instance of {@link PotionBrewer}. 415 * 416 * @return An instance of PotionBrewer 417 */ 418 public static PotionBrewer getBrewer() { 419 return brewer; 420 } 421 422 /** 423 * Sets the current instance of {@link PotionBrewer}. Generally not to be 424 * used from within a plugin. 425 * 426 * @param other The new PotionBrewer 427 */ 428 public static void setPotionBrewer(PotionBrewer other) { 429 if (brewer != null) 430 throw new IllegalArgumentException("brewer can only be set internally"); 431 brewer = other; 432 } 433 434 /** 435 * 436 * @deprecated Magic value 437 */ 438 @Deprecated 439 public int getNameId() { 440 return name; 441 } 442 }