001    package org.bukkit.potion;
002    
003    import java.util.Map;
004    import java.util.NoSuchElementException;
005    
006    import org.apache.commons.lang.Validate;
007    import org.bukkit.configuration.serialization.ConfigurationSerializable;
008    import org.bukkit.configuration.serialization.SerializableAs;
009    import org.bukkit.entity.LivingEntity;
010    
011    import com.google.common.collect.ImmutableMap;
012    
013    /**
014     * Represents a potion effect, that can be added to a {@link LivingEntity}. A
015     * potion effect has a duration that it will last for, an amplifier that will
016     * enhance its effects, and a {@link PotionEffectType}, that represents its
017     * effect on an entity.
018     */
019    @SerializableAs("PotionEffect")
020    public class PotionEffect implements ConfigurationSerializable {
021        private static final String AMPLIFIER = "amplifier";
022        private static final String DURATION = "duration";
023        private static final String TYPE = "effect";
024        private static final String AMBIENT = "ambient";
025        private final int amplifier;
026        private final int duration;
027        private final PotionEffectType type;
028        private final boolean ambient;
029    
030        /**
031         * Creates a potion effect.
032         *
033         * @param type effect type
034         * @param duration measured in ticks, see {@link
035         *     PotionEffect#getDuration()}
036         * @param amplifier the amplifier, see {@link PotionEffect#getAmplifier()}
037         * @param ambient the ambient status, see {@link PotionEffect#isAmbient()}
038         */
039        public PotionEffect(PotionEffectType type, int duration, int amplifier, boolean ambient) {
040            Validate.notNull(type, "effect type cannot be null");
041            this.type = type;
042            this.duration = duration;
043            this.amplifier = amplifier;
044            this.ambient = ambient;
045        }
046    
047        /**
048         * Creates a potion effect. Assumes ambient is true.
049         *
050         * @param type Effect type
051         * @param duration measured in ticks
052         * @param amplifier the amplifier for the effect
053         * @see PotionEffect#PotionEffect(PotionEffectType, int, int, boolean)
054         */
055        public PotionEffect(PotionEffectType type, int duration, int amplifier) {
056            this(type, duration, amplifier, true);
057        }
058    
059        /**
060         * Constructor for deserialization.
061         *
062         * @param map the map to deserialize from
063         */
064        public PotionEffect(Map<String, Object> map) {
065            this(getEffectType(map), getInt(map, DURATION), getInt(map, AMPLIFIER), getBool(map, AMBIENT));
066        }
067    
068        private static PotionEffectType getEffectType(Map<?,?> map) {
069            int type = getInt(map, TYPE);
070            PotionEffectType effect = PotionEffectType.getById(type);
071            if (effect != null) {
072                return effect;
073            }
074            throw new NoSuchElementException(map + " does not contain " + TYPE);
075        }
076    
077        private static int getInt(Map<?,?> map, Object key) {
078            Object num = map.get(key);
079            if (num instanceof Integer) {
080                return (Integer) num;
081            }
082            throw new NoSuchElementException(map + " does not contain " + key);
083        }
084    
085        private static boolean getBool(Map<?,?> map, Object key) {
086            Object bool = map.get(key);
087            if (bool instanceof Boolean) {
088                return (Boolean) bool;
089            }
090            throw new NoSuchElementException(map + " does not contain " + key);
091        }
092    
093        public Map<String, Object> serialize() {
094            return ImmutableMap.<String, Object>of(
095                TYPE, type.getId(),
096                DURATION, duration,
097                AMPLIFIER, amplifier,
098                AMBIENT, ambient
099            );
100        }
101    
102        /**
103         * Attempts to add the effect represented by this object to the given
104         * {@link LivingEntity}.
105         *
106         * @see LivingEntity#addPotionEffect(PotionEffect)
107         * @param entity The entity to add this effect to
108         * @return Whether the effect could be added
109         */
110        public boolean apply(LivingEntity entity) {
111            return entity.addPotionEffect(this);
112        }
113    
114        @Override
115        public boolean equals(Object obj) {
116            if (this == obj) {
117                return true;
118            }
119            if (!(obj instanceof PotionEffect)) {
120                return false;
121            }
122            PotionEffect that = (PotionEffect) obj;
123            return this.type.equals(that.type) && this.ambient == that.ambient && this.amplifier == that.amplifier && this.duration == that.duration;
124        }
125    
126        /**
127         * Returns the amplifier of this effect. A higher amplifier means the
128         * potion effect happens more often over its duration and in some cases
129         * has more effect on its target.
130         *
131         * @return The effect amplifier
132         */
133        public int getAmplifier() {
134            return amplifier;
135        }
136    
137        /**
138         * Returns the duration (in ticks) that this effect will run for when
139         * applied to a {@link LivingEntity}.
140         *
141         * @return The duration of the effect
142         */
143        public int getDuration() {
144            return duration;
145        }
146    
147        /**
148         * Returns the {@link PotionEffectType} of this effect.
149         *
150         * @return The potion type of this effect
151         */
152        public PotionEffectType getType() {
153            return type;
154        }
155    
156        /**
157         * Makes potion effect produce more, translucent, particles.
158         *
159         * @return if this effect is ambient
160         */
161        public boolean isAmbient() {
162            return ambient;
163        }
164    
165        @Override
166        public int hashCode() {
167            int hash = 1;
168            hash = hash * 31 + type.hashCode();
169            hash = hash * 31 + amplifier;
170            hash = hash * 31 + duration;
171            hash ^= 0x22222222 >> (ambient ? 1 : -1);
172            return hash;
173        }
174    
175        @Override
176        public String toString() {
177            return type.getName() + (ambient ? ":(" : ":") + duration + "t-x" + amplifier + (ambient ? ")" : "");
178        }
179    }