From d99c95f6d1b9a91f5f9c254b8e2fa615ea31e3c6 Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 9 Jan 2012 14:39:06 +0800 Subject: [Bleeding] Added Potions API. Fixes BUKKIT-389 --- src/main/java/org/bukkit/entity/LivingEntity.java | 52 ++++ src/main/java/org/bukkit/entity/Player.java | 1 + src/main/java/org/bukkit/entity/ThrownPotion.java | 9 +- src/main/java/org/bukkit/potion/Potion.java | 265 +++++++++++++++++++++ src/main/java/org/bukkit/potion/PotionBrewer.java | 31 +++ src/main/java/org/bukkit/potion/PotionEffect.java | 85 +++++++ .../java/org/bukkit/potion/PotionEffectType.java | 239 +++++++++++++++++++ .../org/bukkit/potion/PotionEffectTypeWrapper.java | 29 +++ src/main/java/org/bukkit/potion/PotionType.java | 45 ++++ .../org/bukkit/plugin/messaging/TestPlayer.java | 27 +++ src/test/java/org/bukkit/potion/PotionTest.java | 109 +++++++++ 11 files changed, 891 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/bukkit/potion/Potion.java create mode 100644 src/main/java/org/bukkit/potion/PotionBrewer.java create mode 100644 src/main/java/org/bukkit/potion/PotionEffect.java create mode 100644 src/main/java/org/bukkit/potion/PotionEffectType.java create mode 100644 src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java create mode 100644 src/main/java/org/bukkit/potion/PotionType.java create mode 100644 src/test/java/org/bukkit/potion/PotionTest.java diff --git a/src/main/java/org/bukkit/entity/LivingEntity.java b/src/main/java/org/bukkit/entity/LivingEntity.java index c57b84d1..a6b4e9b2 100644 --- a/src/main/java/org/bukkit/entity/LivingEntity.java +++ b/src/main/java/org/bukkit/entity/LivingEntity.java @@ -1,10 +1,13 @@ package org.bukkit.entity; +import java.util.Collection; import java.util.HashSet; import java.util.List; import org.bukkit.Location; import org.bukkit.block.Block; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; /** * Represents a living entity, such as a monster or player @@ -225,4 +228,53 @@ public interface LivingEntity extends Entity { * @return Killer player, or null if none found. */ public Player getKiller(); + + /** + * Adds the given {@link PotionEffect} to this entity. + * Only one potion effect can be present for a given {@link PotionEffectType}. + * + * @param effect PotionEffect to be added + * @return Whether the effect could be added + */ + public boolean addPotionEffect(PotionEffect effect); + + /** + * Adds the given {@link PotionEffect} to this entity. + * Only one potion effect can be present for a given {@link PotionEffectType}. + * + * @param effect PotionEffect to be added + * @param force Whether conflicting effects should be removed + * @return Whether the effect could be added + */ + public boolean addPotionEffect(PotionEffect effect, boolean force); + + /** + * Attempts to add all of the given {@link PotionEffect} to this entity. + * + * @param effects The effects to add + * @return Whether all of the effects could be added + */ + public boolean addPotionEffects(Collection effects); + + /** + * Returns whether the entity already has an existing + * effect of the given {@link PotionEffectType} applied to it. + * + * @param type The potion type to check + */ + public boolean hasPotionEffect(PotionEffectType type); + + /** + * Removes any effects present of the given {@link PotionEffectType}. + * + * @param type The potion type to remove + */ + public void removePotionEffect(PotionEffectType type); + + /** + * Returns all currently active {@link PotionEffect}s on this entity. + * + * @return A collection of {@link PotionEffect}s + */ + public Collection getActivePotionEffects(); } diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java index 1ee3722e..c32cd0d9 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -1,6 +1,7 @@ package org.bukkit.entity; import java.net.InetSocketAddress; + import org.bukkit.Achievement; import org.bukkit.ChatColor; import org.bukkit.Effect; diff --git a/src/main/java/org/bukkit/entity/ThrownPotion.java b/src/main/java/org/bukkit/entity/ThrownPotion.java index 789ade54..4a858c6b 100644 --- a/src/main/java/org/bukkit/entity/ThrownPotion.java +++ b/src/main/java/org/bukkit/entity/ThrownPotion.java @@ -1,8 +1,15 @@ package org.bukkit.entity; +import java.util.Collection; + +import org.bukkit.potion.PotionEffect; + /** * Represents a thrown potion bottle */ public interface ThrownPotion extends Projectile { - + /** + * Returns the effects that are applied by this potion. + */ + public Collection getEffects(); } diff --git a/src/main/java/org/bukkit/potion/Potion.java b/src/main/java/org/bukkit/potion/Potion.java new file mode 100644 index 00000000..920522ac --- /dev/null +++ b/src/main/java/org/bukkit/potion/Potion.java @@ -0,0 +1,265 @@ +package org.bukkit.potion; + +import java.util.Collection; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; + +/** + * Represents a minecraft potion + */ +public class Potion { + private boolean extended = false; + private boolean splash = false; + private Tier tier = Tier.ONE; + private final PotionType type; + + public Potion(PotionType type) { + Validate.notNull(type, "type cannot be null"); + this.type = type; + } + + public Potion(PotionType type, Tier tier) { + this(type); + Validate.notNull(tier, "tier cannot be null"); + this.tier = tier; + } + + public Potion(PotionType type, Tier tier, boolean splash) { + this(type, tier); + this.splash = splash; + } + + public Potion(PotionType type, Tier tier, boolean splash, boolean extended) { + this(type, tier, splash); + this.extended = extended; + } + + /** + * Applies the effects of this potion to the given {@link ItemStack}. The + * itemstack must be a potion. + * + * @param to + * The itemstack to apply to + */ + public void apply(ItemStack to) { + Validate.notNull(to, "itemstack cannot be null"); + if (to.getType() != Material.POTION) + throw new IllegalArgumentException("given itemstack is not a potion"); + to.setDurability(toDamageValue()); + } + + /** + * Applies the effects that would be applied by this potion to the given + * {@link LivingEntity}. + * + * @see LivingEntity#addPotionEffects(Collection) + * @param to + * The entity to apply the effects to + */ + public void apply(LivingEntity to) { + Validate.notNull(to, "entity cannot be null"); + to.addPotionEffects(getEffects()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Potion other = (Potion) obj; + return extended == other.extended && splash == other.splash && tier == other.tier && type == other.type; + } + + /** + * Returns a collection of {@link PotionEffect}s that this {@link Potion} + * would confer upon a {@link LivingEntity}. + * + * @see PotionBrewer#getEffectsFromDamage(int) + * @see Potion#toDamageValue() + * @return The effects that this potion applies + */ + public Collection getEffects() { + return getBrewer().getEffectsFromDamage(toDamageValue()); + } + + /** + * Returns the {@link Tier} of this potion. + * + * @return The tier of this potion + */ + public Tier getTier() { + return tier; + } + + /** + * Returns the {@link PotionType} of this potion. + * + * @return The type of this potion + */ + public PotionType getType() { + return type; + } + + /** + * Returns whether this potion has an extended duration. + * + * @return Whether this potion has extended duration + */ + public boolean hasExtendedDuration() { + return extended; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = prime + (extended ? 1231 : 1237); + result = prime * result + (splash ? 1231 : 1237); + result = prime * result + ((tier == null) ? 0 : tier.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + /** + * Returns whether this potion is a splash potion. + * + * @return Whether this is a splash potion + */ + public boolean isSplash() { + return splash; + } + + /** + * Set whether this potion has extended duration. This will cause the potion + * to have roughly 8/3 more duration than a regular potion. + * + * @param isExtended + * Whether the potion should have extended duration + */ + public void setHasExtendedDuration(boolean isExtended) { + extended = isExtended; + } + + /** + * Sets whether this potion is a splash potion. Splash potions can be thrown + * for a radius effect. + * + * @param isSplash + * Whether this is a splash potion + */ + public void setSplash(boolean isSplash) { + splash = isSplash; + } + + /** + * Sets the {@link Tier} of this potion. + * + * @param tier + * The new tier of this potion + */ + public void setTier(Tier tier) { + Validate.notNull(tier, "tier cannot be null"); + this.tier = tier; + } + + /** + * Converts this potion to a valid potion damage short, usable for potion + * item stacks. + * + * @return The damage value of this potion + */ + public short toDamageValue() { + short damage = (short) type.getDamageValue(); + damage |= tier.damageBit; + if (splash) { + damage |= SPLASH_BIT; + } + if (extended) { + damage |= EXTENDED_BIT; + } + return damage; + } + + /** + * Converts this potion to an {@link ItemStack} with the specified amount + * and a correct damage value. + * + * @param amount + * The amount of the ItemStack + * @return The created ItemStack + */ + public ItemStack toItemStack(int amount) { + return new ItemStack(Material.POTION, amount, toDamageValue()); + } + + public enum Tier { + ONE(0), + TWO(0x20); + + private int damageBit; + + Tier(int bit) { + damageBit = bit; + } + + public int getDamageBit() { + return damageBit; + } + + public static Tier getByDamageBit(int damageBit) { + for (Tier tier : Tier.values()) { + if (tier.damageBit == damageBit) + return tier; + } + return null; + } + } + + private static PotionBrewer brewer; + + private static final int EXTENDED_BIT = 0x0040; + private static final int POTION_BIT = 0xF; + private static final int SPLASH_BIT = 0x4000; + + public static Potion fromDamage(int damage) { + PotionType type = PotionType.getByDamageValue(damage & POTION_BIT); + Validate.notNull(type, "unable to find potion type"); + Tier tier = Tier.getByDamageBit(damage & Tier.TWO.damageBit); + Validate.notNull(tier, "unable to find tier"); + return new Potion(type, tier, (damage & SPLASH_BIT) > 0, (damage & EXTENDED_BIT) > 0); + } + + public static Potion fromItemStack(ItemStack item) { + Validate.notNull(item, "item cannot be null"); + if (item.getType() != Material.POTION) + throw new IllegalArgumentException("item is not a potion"); + return fromDamage(item.getDurability()); + } + + /** + * Returns an instance of {@link PotionBrewer}. + * + * @return An instance of PotionBrewer + */ + public static PotionBrewer getBrewer() { + return brewer; + } + + /** + * Sets the current instance of {@link PotionBrewer}. Generally not to be + * used from within a plugin. + * + * @param other + * The new PotionBrewer + */ + public static void setPotionBrewer(PotionBrewer other) { + if (brewer != null) + throw new IllegalArgumentException("brewer can only be set internally"); + brewer = other; + } +} \ No newline at end of file diff --git a/src/main/java/org/bukkit/potion/PotionBrewer.java b/src/main/java/org/bukkit/potion/PotionBrewer.java new file mode 100644 index 00000000..14df6a05 --- /dev/null +++ b/src/main/java/org/bukkit/potion/PotionBrewer.java @@ -0,0 +1,31 @@ +package org.bukkit.potion; + +import java.util.Collection; + +/** + * Represents a brewer that can create {@link PotionEffect}s. + */ +public interface PotionBrewer { + /** + * Creates a {@link PotionEffect} from the given {@link PotionEffectType}, + * applying duration modifiers and checks. + * + * @param potion + * The type of potion + * @param duration + * The duration in ticks + * @param amplifier + * The amplifier of the effect + */ + public PotionEffect createEffect(PotionEffectType potion, int duration, int amplifier); + + /** + * Returns a collection of {@link PotionEffect} that would be applied from a + * potion with the given data value. + * + * @param damage + * The data value of the potion + * @return + */ + public Collection getEffectsFromDamage(int damage); +} diff --git a/src/main/java/org/bukkit/potion/PotionEffect.java b/src/main/java/org/bukkit/potion/PotionEffect.java new file mode 100644 index 00000000..e4066323 --- /dev/null +++ b/src/main/java/org/bukkit/potion/PotionEffect.java @@ -0,0 +1,85 @@ +package org.bukkit.potion; + +import org.apache.commons.lang.Validate; +import org.bukkit.entity.LivingEntity; + +/** + * Represents a potion effect, that can be added to a {@link LivingEntity}. A + * potion effect has a duration that it will last for, an amplifier that will + * enhance its effects, and a {@link PotionEffectType}, that represents its + * effect on an entity. + */ +public class PotionEffect { + private final int amplifier; + private final int duration; + private final PotionEffectType type; + + public PotionEffect(PotionEffectType type, int duration, int amplifier) { + Validate.notNull(type, "effect type cannot be null"); + this.type = type; + this.duration = duration; + this.amplifier = amplifier; + } + + /** + * Attempts to add the effect represented by this object to the given + * {@link LivingEntity}. + * + * @see LivingEntity#addPotionEffect(PotionEffect) + * @param entity + * The entity to add this effect to + */ + public boolean apply(LivingEntity entity) { + return entity.addPotionEffect(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + PotionEffect other = (PotionEffect) obj; + if (type == null) { + if (other.type != null) { + return false; + } + } else if (!type.equals(other.type)) { + return false; + } + return true; + } + + /** + * Returns the amplifier of this effect. A higher amplifier means the potion + * effect happens more often over its duration and in some cases has more + * effect on its target. + */ + public int getAmplifier() { + return amplifier; + } + + /** + * Returns the duration (in ticks) that this effect will run for when + * applied to a {@link LivingEntity}. + */ + public int getDuration() { + return duration; + } + + /** + * Returns the {@link PotionEffectType} of this effect. + * + * @return The potion type of this effect + */ + public PotionEffectType getType() { + return type; + } + + @Override + public int hashCode() { + return 31 + ((type == null) ? 0 : type.hashCode()); + }; +} diff --git a/src/main/java/org/bukkit/potion/PotionEffectType.java b/src/main/java/org/bukkit/potion/PotionEffectType.java new file mode 100644 index 00000000..d43aea5e --- /dev/null +++ b/src/main/java/org/bukkit/potion/PotionEffectType.java @@ -0,0 +1,239 @@ +package org.bukkit.potion; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.Validate; + +/** + * Represents a type of potion and its effect on an entity. + */ +public abstract class PotionEffectType { + /** + * Increases movement speed. + */ + public static PotionEffectType SPEED = new PotionEffectTypeWrapper(1); + + /** + * Decreases movement speed. + */ + public static PotionEffectType SLOW = new PotionEffectTypeWrapper(2); + + /** + * Increases dig speed. + */ + public static PotionEffectType FAST_DIGGING = new PotionEffectTypeWrapper(3); + + /** + * Decreases dig speed. + */ + public static PotionEffectType SLOW_DIGGING = new PotionEffectTypeWrapper(4); + + /** + * Increases damage dealt. + */ + public static PotionEffectType INCREASE_DAMAGE = new PotionEffectTypeWrapper(5); + + /** + * Heals an entity. + */ + public static PotionEffectType HEAL = new PotionEffectTypeWrapper(6); + + /** + * Hurts an entity. + */ + public static PotionEffectType HARM = new PotionEffectTypeWrapper(7); + + /** + * Increases jump height. + */ + public static PotionEffectType JUMP = new PotionEffectTypeWrapper(8); + + /** + * Warps vision on the client. + */ + public static PotionEffectType CONFUSION = new PotionEffectTypeWrapper(9); + + /** + * Regenerates health. + */ + public static PotionEffectType REGENERATION = new PotionEffectTypeWrapper(10); + + /** + * Decreases damage dealt to an entity. + */ + public static PotionEffectType DAMAGE_RESISTANCE = new PotionEffectTypeWrapper(11); + + /** + * Stops fire damage. + */ + public static PotionEffectType FIRE_RESISTANCE = new PotionEffectTypeWrapper(12); + + /** + * Allows breathing underwater. + */ + public static PotionEffectType WATER_BREATHING = new PotionEffectTypeWrapper(13); + + /** + * Grants invisibility. + */ + @Deprecated + public static PotionEffectType INVISIBILITY = new PotionEffectTypeWrapper(14); // unimplemented + + /** + * Blinds an entity. + */ + public static PotionEffectType BLINDNESS = new PotionEffectTypeWrapper(15); + + /** + * Allows an entity to see in the dark. + */ + @Deprecated + public static PotionEffectType NIGHT_VISION = new PotionEffectTypeWrapper(16); // unimplemented + + /** + * Increases hunger. + */ + public static PotionEffectType HUNGER = new PotionEffectTypeWrapper(17); + + /** + * Decreases damage dealt by an entity. + */ + public static PotionEffectType WEAKNESS = new PotionEffectTypeWrapper(18); + + /** + * Deals damage to an entity over time. + */ + public static PotionEffectType POISON = new PotionEffectTypeWrapper(19); + + private final int id; + + protected PotionEffectType(int id) { + this.id = id; + } + + public PotionEffect createEffect(int duration, int amplifier) { + return Potion.getBrewer().createEffect(this, duration, amplifier); + } + + /** + * Returns the duration modifier applied to effects of this type. + * + * @return duration modifier + */ + public abstract double getDurationModifier(); + + /** + * Returns the unique ID of this type. + * + * @return Unique ID + */ + public int getId() { + return id; + } + + /** + * Returns the name of this effect type. + * + * @return The name of this effect type + */ + public abstract String getName(); + + /** + * Returns whether the effect of this type happens once, immediately. + * + * @return whether this type is normally instant + */ + public abstract boolean isInstant(); + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof PotionEffectType)) { + return false; + } + final PotionEffectType other = (PotionEffectType) obj; + if (this.id != other.id) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public String toString() { + return "PotionEffectType[" + id + ", " + getName() + "]"; + } + + private static final PotionEffectType[] byId = new PotionEffectType[20]; + private static final Map byName = new HashMap(); + // will break on updates. + private static boolean acceptingNew = true; + + /** + * Gets the effect type specified by the unique id. + * + * @param id + * Unique ID to fetch + * @return Resulting type, or null if not found. + */ + public static PotionEffectType getById(int id) { + if (id >= byId.length || id < 0) + return null; + return byId[id]; + } + + /** + * Gets the effect type specified by the given name. + * + * @param name + * Name of PotionEffectType to fetch + * @return Resulting PotionEffectType, or null if not found. + */ + public static PotionEffectType getByName(String name) { + Validate.notNull(name, "name cannot be null"); + return byName.get(name.toLowerCase()); + } + + /** + * Registers an effect type with the given object. + *

+ * Generally not to be used from within a plugin. + * + * @param potionType + * PotionType to register + */ + public static void registerPotionEffectType(PotionEffectType type) { + if (byId[type.id] != null || byName.containsKey(type.getName().toLowerCase())) { + throw new IllegalArgumentException("Cannot set already-set type"); + } else if (!acceptingNew) { + throw new IllegalStateException( + "No longer accepting new potion effect types (can only be done by the server implementation)"); + } + + byId[type.id] = type; + byName.put(type.getName().toLowerCase(), type); + } + + /** + * Stops accepting any effect type registrations. + */ + public static void stopAcceptingRegistrations() { + acceptingNew = false; + } + + /** + * Returns an array of all the registered {@link PotionEffectType}s. + * + * @return Array of types. + */ + public static PotionEffectType[] values() { + return byId.clone(); + } +} diff --git a/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java b/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java new file mode 100644 index 00000000..d1cc99f3 --- /dev/null +++ b/src/main/java/org/bukkit/potion/PotionEffectTypeWrapper.java @@ -0,0 +1,29 @@ +package org.bukkit.potion; + +public class PotionEffectTypeWrapper extends PotionEffectType { + protected PotionEffectTypeWrapper(int id) { + super(id); + } + + @Override + public double getDurationModifier() { + return getType().getDurationModifier(); + } + + @Override + public String getName() { + return getType().getName(); + } + + /** + * Get the potion type bound to this wrapper. + */ + public PotionEffectType getType() { + return PotionEffectType.getById(getId()); + } + + @Override + public boolean isInstant() { + return getType().isInstant(); + } +} diff --git a/src/main/java/org/bukkit/potion/PotionType.java b/src/main/java/org/bukkit/potion/PotionType.java new file mode 100644 index 00000000..851a5d99 --- /dev/null +++ b/src/main/java/org/bukkit/potion/PotionType.java @@ -0,0 +1,45 @@ +package org.bukkit.potion; + +public enum PotionType { + REGEN(1, PotionEffectType.REGENERATION), + SPEED(2, PotionEffectType.SPEED), + FIRE_RESISTANCE(3, PotionEffectType.FIRE_RESISTANCE), + POISON(4, PotionEffectType.POISON), + INSTANT_HEAL(5, PotionEffectType.HEAL), + WEAKNESS(8, PotionEffectType.SPEED), + STRENGTH(9, PotionEffectType.INCREASE_DAMAGE), + SLOWNESS(10, PotionEffectType.SLOW), + INSTANT_DAMAGE(12, PotionEffectType.HARM); + + private final int damageValue; + private final PotionEffectType effect; + + PotionType(int damageValue, PotionEffectType effect) { + this.damageValue = damageValue; + this.effect = effect; + } + + public PotionEffectType getEffectType() { + return effect; + } + + protected int getDamageValue() { + return damageValue; + } + + public static PotionType getByDamageValue(int damage) { + for (PotionType type : PotionType.values()) { + if (type.damageValue == damage) + return type; + } + return null; + } + + public static PotionType getByEffect(PotionEffectType effectType) { + for (PotionType type : PotionType.values()) { + if (type.effect.equals(effectType)) + return type; + } + return null; + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java index 95e02c76..63180720 100644 --- a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java +++ b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java @@ -1,6 +1,7 @@ package org.bukkit.plugin.messaging; import java.net.InetSocketAddress; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -23,6 +24,8 @@ import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.Plugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; public class TestPlayer implements Player { @@ -621,4 +624,28 @@ public class TestPlayer implements Player { public boolean canSee(Player player) { throw new UnsupportedOperationException("Not supported yet."); } + + public boolean addPotionEffect(PotionEffect effect) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean addPotionEffect(PotionEffect effect, boolean force) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean addPotionEffects(Collection effects) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean hasPotionEffect(PotionEffectType type) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void removePotionEffect(PotionEffectType type) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Collection getActivePotionEffects() { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java new file mode 100644 index 00000000..fe641b86 --- /dev/null +++ b/src/test/java/org/bukkit/potion/PotionTest.java @@ -0,0 +1,109 @@ +package org.bukkit.potion; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.is; + +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.Potion.Tier; +import org.junit.Test; + +public class PotionTest { + @Test + public void applyToItemStack() { + Potion potion = new Potion(PotionType.POISON); + ItemStack stack = new ItemStack(Material.POTION, 1); + potion.apply(stack); + assertTrue(stack.getDurability() == potion.toDamageValue()); + } + + @Test + public void fromDamage() { + Potion potion = Potion.fromDamage(PotionType.POISON.getDamageValue()); + assertTrue(potion.getType() == PotionType.POISON); + potion = Potion.fromDamage(PotionType.POISON.getDamageValue() | SPLASH_BIT); + assertTrue(potion.getType() == PotionType.POISON && potion.isSplash()); + potion = Potion.fromDamage(0x25 /* Potion of Healing II */); + assertTrue(potion.getType() == PotionType.INSTANT_HEAL && potion.getTier() == Tier.TWO); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalApplyToItemStack() { + Potion potion = new Potion(PotionType.POISON); + potion.apply(new ItemStack(Material.AIR, 1)); + } + + @Test + public void ItemStackConversion() { + Potion potion = new Potion(PotionType.POISON); + ItemStack itemstack = potion.toItemStack(1); + assertThat(itemstack.getType(), is(Material.POTION)); + assertTrue(itemstack.getAmount() == 1); + assertTrue(itemstack.getDurability() == potion.toDamageValue()); + } + + @Test + public void setExtended() { + Potion potion = new Potion(PotionType.POISON); + assertFalse(potion.hasExtendedDuration()); + potion.setHasExtendedDuration(true); + assertTrue(potion.hasExtendedDuration()); + assertTrue((potion.toDamageValue() & EXTENDED_BIT) != 0); + } + + @Test + public void setSplash() { + Potion potion = new Potion(PotionType.POISON); + assertFalse(potion.isSplash()); + potion.setSplash(true); + assertTrue(potion.isSplash()); + assertTrue((potion.toDamageValue() & SPLASH_BIT) != 0); + } + + @Test + public void setTier() { + Potion potion = new Potion(PotionType.POISON); + assertThat(potion.getTier(), is(Tier.ONE)); + potion.setTier(Tier.TWO); + assertThat(potion.getTier(), is(Tier.TWO)); + assertTrue(potion.toDamageValue() == (PotionType.POISON.getDamageValue() | potion.getTier().getDamageBit())); + } + + @Test + public void useNulls() { + try { + new Potion(null); + fail("cannot use null type in constructor"); + } catch (IllegalArgumentException ex) { + } + + try { + new Potion(PotionType.POISON, null); + fail("cannot use null tier in constructor"); + } catch (IllegalArgumentException ex) { + } + + Potion potion = new Potion(PotionType.POISON); + try { + potion.setTier(null); + fail("cannot set a null tier"); + } catch (IllegalArgumentException ex) { + } + + try { + potion.apply((ItemStack) null); + fail("cannot apply to a null itemstack"); + } catch (IllegalArgumentException ex) { + } + + try { + potion.apply((LivingEntity) null); + fail("cannot apply to a null entity"); + } catch (IllegalArgumentException ex) { + } + } + + private static final int EXTENDED_BIT = 0x40; + private static final int SPLASH_BIT = 0x4000; +} -- cgit v1.2.3