[Bleeding] Add ability for the Potion class to represent no-effect

potions. Addresses BUKKIT-817

- Potion's type can be null
- Add Potion.setType()
- New Potion constructors which do not take a PotionType parameter
- Potion constructor chainers splash() and extend() added
- Add PotionType.getMaxLevel() and PotionType.isInstant()
- Add PotionType.WATER which is a very special case
- Make PotionType.getDamageValue() public
This commit is contained in:
Celtic Minstrel 2012-02-24 23:18:13 -05:00 committed by EvilSeph
parent 569662343b
commit bffb985039
2 changed files with 142 additions and 23 deletions

View File

@ -1,6 +1,7 @@
package org.bukkit.potion; package org.bukkit.potion;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Material; import org.bukkit.Material;
@ -16,46 +17,115 @@ public class Potion {
@Deprecated @Deprecated
private Tier tier = Tier.ONE; private Tier tier = Tier.ONE;
private int level = 1; private int level = 1;
private final PotionType type; private int name = -1;
private PotionType type;
/**
* Construct a new potion of the given type. Unless the type is {@link PotionType#WATER},
* it will be level one, without extended duration. Don't use this constructor to create
* a no-effect potion other than water bottle.
* @param type The potion type
* @see #Potion(int)
*/
public Potion(PotionType type) { public Potion(PotionType type) {
Validate.notNull(type, "type cannot be null");
this.type = type; this.type = type;
if (type != null) {
this.name = type.getDamageValue();
}
} }
/** @deprecated In favour of {@link #Potion(PotionType, int)} */
@Deprecated @Deprecated
public Potion(PotionType type, Tier tier) { public Potion(PotionType type, Tier tier) {
this(type, tier == Tier.TWO ? 2 : 1); this(type, tier == Tier.TWO ? 2 : 1);
Validate.notNull(tier, "tier cannot be null"); Validate.notNull(type, "Type cannot be null");
} }
/** @deprecated In favour of {@link #Potion(PotionType, int, boolean)} */
@Deprecated @Deprecated
public Potion(PotionType type, Tier tier, boolean splash) { public Potion(PotionType type, Tier tier, boolean splash) {
this(type, tier == Tier.TWO ? 2 : 1, splash); this(type, tier == Tier.TWO ? 2 : 1, splash);
} }
/** @deprecated In favour of {@link #Potion(PotionType, int, boolean, boolean)} */
@Deprecated @Deprecated
public Potion(PotionType type, Tier tier, boolean splash, boolean extended) { public Potion(PotionType type, Tier tier, boolean splash, boolean extended) {
this(type, tier, splash); this(type, tier, splash);
this.extended = extended; this.extended = extended;
} }
/**
* Create a new potion of the given type and level.
* @param type The type of potion.
* @param level The potion's level.
*/
public Potion(PotionType type, int level) { public Potion(PotionType type, int level) {
this(type); this(type);
Validate.notNull(type, "Type cannot be null");
Validate.isTrue(type != PotionType.WATER, "Water bottles don't have a level!");
Validate.isTrue(level > 0 && level < 3, "Level must be 1 or 2"); Validate.isTrue(level > 0 && level < 3, "Level must be 1 or 2");
this.level = level; this.level = level;
} }
/**
* Create a new potion of the given type and level.
* @param type The type of potion.
* @param level The potion's level.
* @param splash Whether it is a splash potion.
* @deprecated In favour of using {@link #Potion(PotionType)} with {@link #splash()}.
*/
@Deprecated
public Potion(PotionType type, int level, boolean splash) { public Potion(PotionType type, int level, boolean splash) {
this(type, level); this(type, level);
this.splash = splash; this.splash = splash;
} }
/**
* Create a new potion of the given type and level.
* @param type The type of potion.
* @param level The potion's level.
* @param splash Whether it is a splash potion.
* @param extended Whether it has an extended duration.
* @deprecated In favour of using {@link #Potion(PotionType)} with {@link #extended}
* and possibly {@link #splash()}.
*/
@Deprecated
public Potion(PotionType type, int level, boolean splash, boolean extended) { public Potion(PotionType type, int level, boolean splash, boolean extended) {
this(type, level, splash); this(type, level, splash);
this.extended = extended; this.extended = extended;
} }
/**
* Create a potion with a specific name.
* @param name The name index (0-63)
*/
public Potion(int name) {
this(PotionType.getByDamageValue(name & POTION_BIT));
this.name = name & NAME_BIT;
if (name == 0) {
// If it's 0 it would've become PotionType.WATER, but it should actually be mundane potion
this.type = null;
}
}
/**
* Chain this to the constructor to make the potion a splash potion.
* @return The potion.
*/
public Potion splash() {
setSplash(true);
return this;
}
/**
* Chain this to the constructor to extend the potion's duration.
* @return The potion.
*/
public Potion extend() {
setHasExtendedDuration(true);
return this;
}
/** /**
* Applies the effects of this potion to the given {@link ItemStack}. The * Applies the effects of this potion to the given {@link ItemStack}. The
* itemstack must be a potion. * itemstack must be a potion.
@ -65,8 +135,7 @@ public class Potion {
*/ */
public void apply(ItemStack to) { public void apply(ItemStack to) {
Validate.notNull(to, "itemstack cannot be null"); Validate.notNull(to, "itemstack cannot be null");
if (to.getType() != Material.POTION) Validate.isTrue(to.getType() == Material.POTION, "given itemstack is not a potion");
throw new IllegalArgumentException("given itemstack is not a potion");
to.setDurability(toDamageValue()); to.setDurability(toDamageValue());
} }
@ -103,7 +172,9 @@ public class Potion {
* @see Potion#toDamageValue() * @see Potion#toDamageValue()
* @return The effects that this potion applies * @return The effects that this potion applies
*/ */
@SuppressWarnings("unchecked")
public Collection<PotionEffect> getEffects() { public Collection<PotionEffect> getEffects() {
if(type == null) return Collections.EMPTY_SET;
return getBrewer().getEffectsFromDamage(toDamageValue()); return getBrewer().getEffectsFromDamage(toDamageValue());
} }
@ -171,6 +242,7 @@ public class Potion {
* Whether the potion should have extended duration * Whether the potion should have extended duration
*/ */
public void setHasExtendedDuration(boolean isExtended) { public void setHasExtendedDuration(boolean isExtended) {
Validate.isTrue(type == null || !type.isInstant(), "Instant potions cannot be extended");
extended = isExtended; extended = isExtended;
} }
@ -199,6 +271,16 @@ public class Potion {
this.level = (tier == Tier.TWO ? 2 : 1); this.level = (tier == Tier.TWO ? 2 : 1);
} }
/**
* Sets the {@link PotionType} of this potion.
*
* @param type
* The new type of this potion
*/
public void setType(PotionType type) {
this.type = type;
}
/** /**
* Sets the level of this potion. * Sets the level of this potion.
* *
@ -206,6 +288,9 @@ public class Potion {
* The new level of this potion * The new level of this potion
*/ */
public void setLevel(int level) { public void setLevel(int level) {
Validate.notNull(this.type, "No-effect potions don't have a level.");
int max = type.getMaxLevel();
Validate.isTrue(level > 0 && level <= max, "Level must be " + (max == 1 ? "" : "between 1 and ") + max + " for this potion");
this.level = level; this.level = level;
this.tier = level == 2 ? Tier.TWO : Tier.ONE; this.tier = level == 2 ? Tier.TWO : Tier.ONE;
} }
@ -217,8 +302,17 @@ public class Potion {
* @return The damage value of this potion * @return The damage value of this potion
*/ */
public short toDamageValue() { public short toDamageValue() {
short damage = type == null ? 0 : (short) type.getDamageValue(); short damage;
damage |= level == 2 ? 0x20 : 0; if (type == PotionType.WATER) {
return 0;
} else if (type == null) {
// Without this, mundanePotion.toDamageValue() would return 0
damage = (short) (name == 0 ? 8192 : name);
} else {
damage = (short) level;
damage = (short) type.getDamageValue();
damage |= level << TIER_SHIFT;
}
if (splash) { if (splash) {
damage |= SPLASH_BIT; damage |= SPLASH_BIT;
} }
@ -271,12 +365,25 @@ public class Potion {
private static final int SPLASH_BIT = 0x4000; private static final int SPLASH_BIT = 0x4000;
private static final int TIER_BIT = 0x20; private static final int TIER_BIT = 0x20;
private static final int TIER_SHIFT = 5; private static final int TIER_SHIFT = 5;
private static final int NAME_BIT = 0x3F;
public static Potion fromDamage(int damage) { public static Potion fromDamage(int damage) {
PotionType type = PotionType.getByDamageValue(damage & POTION_BIT); PotionType type = PotionType.getByDamageValue(damage & POTION_BIT);
int level = 1; Potion potion;
level = (damage & TIER_BIT) >> TIER_SHIFT; if (type == null) {
return new Potion(type, level, (damage & SPLASH_BIT) > 0, (damage & EXTENDED_BIT) > 0); potion = new Potion(damage & NAME_BIT);
} else {
int level = (damage & TIER_BIT) >> TIER_SHIFT;
level++;
potion = new Potion(type, level);
}
if ((damage & SPLASH_BIT) > 0) {
potion = potion.splash();
}
if ((damage & EXTENDED_BIT) > 0) {
potion = potion.extend();
}
return potion;
} }
public static Potion fromItemStack(ItemStack item) { public static Potion fromItemStack(ItemStack item) {

View File

@ -1,32 +1,42 @@
package org.bukkit.potion; package org.bukkit.potion;
public enum PotionType { public enum PotionType {
REGEN(1, PotionEffectType.REGENERATION), WATER(0, null, 0),
SPEED(2, PotionEffectType.SPEED), REGEN(1, PotionEffectType.REGENERATION, 2),
FIRE_RESISTANCE(3, PotionEffectType.FIRE_RESISTANCE), SPEED(2, PotionEffectType.SPEED, 2),
POISON(4, PotionEffectType.POISON), FIRE_RESISTANCE(3, PotionEffectType.FIRE_RESISTANCE, 1),
INSTANT_HEAL(5, PotionEffectType.HEAL), POISON(4, PotionEffectType.POISON, 2),
WEAKNESS(8, PotionEffectType.SPEED), INSTANT_HEAL(5, PotionEffectType.HEAL, 2),
STRENGTH(9, PotionEffectType.INCREASE_DAMAGE), WEAKNESS(8, PotionEffectType.SPEED, 1),
SLOWNESS(10, PotionEffectType.SLOW), STRENGTH(9, PotionEffectType.INCREASE_DAMAGE, 2),
INSTANT_DAMAGE(12, PotionEffectType.HARM); SLOWNESS(10, PotionEffectType.SLOW, 1),
INSTANT_DAMAGE(12, PotionEffectType.HARM, 2);
private final int damageValue; private final int damageValue, maxLevel;
private final PotionEffectType effect; private final PotionEffectType effect;
PotionType(int damageValue, PotionEffectType effect) { PotionType(int damageValue, PotionEffectType effect, int maxLevel) {
this.damageValue = damageValue; this.damageValue = damageValue;
this.effect = effect; this.effect = effect;
this.maxLevel = maxLevel;
} }
public PotionEffectType getEffectType() { public PotionEffectType getEffectType() {
return effect; return effect;
} }
protected int getDamageValue() { public int getDamageValue() {
return damageValue; return damageValue;
} }
public int getMaxLevel() {
return maxLevel;
}
public boolean isInstant() {
return effect == null ? true : effect.isInstant();
}
public static PotionType getByDamageValue(int damage) { public static PotionType getByDamageValue(int damage) {
for (PotionType type : PotionType.values()) { for (PotionType type : PotionType.values()) {
if (type.damageValue == damage) if (type.damageValue == damage)
@ -36,8 +46,10 @@ public enum PotionType {
} }
public static PotionType getByEffect(PotionEffectType effectType) { public static PotionType getByEffect(PotionEffectType effectType) {
if (effectType == null)
return WATER;
for (PotionType type : PotionType.values()) { for (PotionType type : PotionType.values()) {
if (type.effect.equals(effectType)) if (effectType.equals(type.effect))
return type; return type;
} }
return null; return null;