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 }