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 }