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    }