package com.laytonsmith.core.functions; import com.laytonsmith.abstraction.MCEnchantment; import com.laytonsmith.abstraction.MCItemStack; import com.laytonsmith.abstraction.MCPlayer; import com.laytonsmith.abstraction.StaticLayer; import com.laytonsmith.annotations.api; import com.laytonsmith.core.CHVersion; import com.laytonsmith.core.Static; import com.laytonsmith.core.constructs.CArray; import com.laytonsmith.core.constructs.CBoolean; import com.laytonsmith.core.constructs.CInt; import com.laytonsmith.core.constructs.CNull; import com.laytonsmith.core.constructs.CString; import com.laytonsmith.core.constructs.CVoid; import com.laytonsmith.core.constructs.Construct; import com.laytonsmith.core.constructs.Target; import com.laytonsmith.core.environments.CommandHelperEnvironment; import com.laytonsmith.core.environments.Environment; import com.laytonsmith.core.exceptions.CRE.CRECastException; import com.laytonsmith.core.exceptions.CRE.CREEnchantmentException; import com.laytonsmith.core.exceptions.CRE.CREFormatException; import com.laytonsmith.core.exceptions.CRE.CREPlayerOfflineException; import com.laytonsmith.core.exceptions.CRE.CRERangeException; import com.laytonsmith.core.exceptions.CRE.CREThrowable; import com.laytonsmith.core.exceptions.ConfigRuntimeException; import java.util.HashMap; import java.util.Map; /** * */ public class Enchantments { public static String docs() { return "Provides methods for dealing with enchanted items"; } /** * Converts the wiki version string to the bukkit version string. If the * specified string isn't in the wiki, the string is returned unchanged. * * @param wikiVersion * @return */ public static String ConvertName(String wikiVersion) { String lc = wikiVersion.toUpperCase().trim(); if (lc.equals("PROTECTION")) { return "PROTECTION_ENVIRONMENTAL"; } else if (lc.equals("FIRE PROTECTION")) { return "PROTECTION_FIRE"; } else if (lc.equals("FEATHER FALLING")) { return "PROTECTION_FALL"; } else if (lc.equals("BLAST PROTECTION")) { return "PROTECTION_EXPLOSIONS"; } else if (lc.equals("PROJECTILE PROTECTION")) { return "PROTECTION_PROJECTILE"; } else if (lc.equals("RESPIRATION")) { return "OXYGEN"; } else if (lc.equals("AQUA AFFINITY")) { return "WATER_WORKER"; } else if (lc.equals("SHARPNESS")) { return "DAMAGE_ALL"; } else if (lc.equals("SMITE")) { return "DAMAGE_UNDEAD"; } else if (lc.equals("BANE OF ARTHROPODS")) { return "DAMAGE_ARTHROPODS"; } else if (lc.equals("KNOCKBACK")) { return "KNOCKBACK"; } else if (lc.equals("FIRE ASPECT")) { return "FIRE_ASPECT"; } else if (lc.equals("LOOTING")) { return "LOOT_BONUS_MOBS"; } else if (lc.equals("EFFICIENCY")) { return "DIG_SPEED"; } else if (lc.equals("SILK TOUCH")) { return "SILK_TOUCH"; } else if (lc.equals("UNBREAKING")) { return "DURABILITY"; } else if (lc.equals("FORTUNE")) { return "LOOT_BONUS_BLOCKS"; } else if (lc.equals("POWER")){ return "ARROW_DAMAGE"; } else if(lc.equals("PUNCH")){ return "ARROW_KNOCKBACK"; } else if(lc.equals("FLAME")){ return "ARROW_FIRE"; } else if(lc.equals("INFINITY")){ return "ARROW_INFINITE"; } else { return wikiVersion; } } /** * Converts the roman numeral into an integer (as a string). If the value * passed in is already an integer, it is returned as is. * * @param romanNumeral * @return */ public static String ConvertLevel(String romanNumeral) { String lc = romanNumeral.toLowerCase().trim(); try { Integer.parseInt(lc); return lc; } catch (NumberFormatException e) { //Maybe roman numeral? } int i = romanToWestern(lc); // if(lc.equals("i")){ // i = 1; // } else if(lc.equals("ii")){ // i = 2; // } else if(lc.equals("iii")){ // i = 3; // } else if(lc.equals("iv")){ // i = 4; // } else if(lc.equals("v")){ // i = 5; // } else { // return romanNumeral; // } return Integer.toString(i); } public static int romanToWestern(String roman) { int western = 0; //the numerical version char currentChar; char nextChar; int i = 0; while (i < roman.length()) { currentChar = roman.charAt(i); if (i < roman.length() - 1) { nextChar = roman.charAt(i + 1); if (getValue(currentChar) < getValue(nextChar)) { western += (getValue(nextChar) - getValue(currentChar)); i += 2; } else { western += getValue(currentChar); i++; } } else { western += getValue(currentChar); i++; } } return western; } private static int getValue(char l) { //Converts the numeral to a number String letter = String.valueOf(l); if (letter.equalsIgnoreCase("I")) { return 1; } if (letter.equalsIgnoreCase("V")) { return 5; } if (letter.equalsIgnoreCase("X")) { return 10; } if (letter.equalsIgnoreCase("L")) { return 50; } if (letter.equalsIgnoreCase("C")) { return 100; } if (letter.equalsIgnoreCase("D")) { return 500; } if (letter.equalsIgnoreCase("M")) { return 1000; } return 0; } @api(environments={CommandHelperEnvironment.class}) public static class enchant_inv extends AbstractFunction { @Override public String getName() { return "enchant_inv"; } @Override public Integer[] numArgs() { return new Integer[]{3, 4}; } @Override public String docs() { return "void {[player], slot, type, level} Adds an enchantment to an item in the player's inventory. Type can be a single string," + " or an array of enchantment names. If slot is null, the currently selected slot is used. If the enchantment cannot be applied" + " to the specified item, an EnchantmentException is thrown, and if the level specified is not valid, a RangeException is thrown." + " If type is an array, level must also be an array, with equal number of values in it, with each int corresponding to the appropriate" + " type. You may use either the bukkit names for enchantments, or the name shown on the wiki: [http://www.minecraftwiki.net/wiki/Enchanting#Enchantment_Types]," + " and level may be a roman numeral as well."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CRECastException.class, CREEnchantmentException.class, CREPlayerOfflineException.class}; } @Override public boolean isRestricted() { return true; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { MCPlayer m = environment.getEnv(CommandHelperEnvironment.class).GetPlayer(); int offset = 1; if (args.length == 4) { m = Static.GetPlayer(args[0].val(), t); offset = 0; } else if (m == null) { throw new CREPlayerOfflineException("Invalid sender!", t); } MCItemStack is = m.getItemAt(args[1 - offset] instanceof CNull ? null : Static.getInt32(args[1 - offset], t)); if (is == null) { throw new CRECastException("There is no item at slot " + args[1 - offset], t); } // if (args[1 - offset] instanceof CNull) { // is = m.getItemInHand(); // } else { // int slot = Static.getInt32(args[1 - offset]); // is = m.getInventory().getItem(slot); // } CArray enchantArray = new CArray(t); if (!(args[2 - offset] instanceof CArray)) { enchantArray.push(args[2 - offset], t); } else { enchantArray = (CArray) args[2 - offset]; } CArray levelArray = new CArray(t); if (!(args[3 - offset] instanceof CArray)) { levelArray.push(args[3 - offset], t); } else { levelArray = (CArray) args[3 - offset]; } for (String key : enchantArray.stringKeySet()) { MCEnchantment e = StaticLayer.GetEnchantmentByName(Enchantments.ConvertName(enchantArray.get(key, t).val())); if (e == null) { throw new CREEnchantmentException(enchantArray.get(key, t).val().toUpperCase() + " is not a valid enchantment type", t); } if (e.canEnchantItem(is)) { int level = Static.getInt32(new CString(Enchantments.ConvertLevel(levelArray.get(key, t).val()), t), t); if (e.getMaxLevel() >= level && level > 0) { is.addEnchantment(e, level); } else { throw new CRERangeException("Level must be greater than 0, and less than " + e.getMaxLevel() + " but was " + level, t); } } else { throw new CREEnchantmentException(enchantArray.get(key, t).val().toUpperCase() + " cannot be applied to this item", t); } } return CVoid.VOID; } } @api(environments={CommandHelperEnvironment.class}) public static class enchant_rm_inv extends AbstractFunction { @Override public String getName() { return "enchant_rm_inv"; } @Override public Integer[] numArgs() { return new Integer[]{2, 3}; } @Override public String docs() { return "void {[player], slot, type} Removes an enchantment from an item. type may be a valid enchantment, or an array of enchantment names. It" + " can also be null, and all enchantments will be removed. If an enchantment is specified, and the item is not enchanted with that," + " it is simply ignored."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CRECastException.class, CREEnchantmentException.class, CREPlayerOfflineException.class}; } @Override public boolean isRestricted() { return true; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { MCPlayer m = environment.getEnv(CommandHelperEnvironment.class).GetPlayer(); int offset = 1; if (args.length == 3) { m = Static.GetPlayer(args[0].val(), t); offset = 0; } Static.AssertPlayerNonNull(m, t); MCItemStack is = m.getItemAt(args[1 - offset] instanceof CNull?null:Static.getInt32(args[1 - offset], t)); if (is == null) { throw new CRECastException("There is no item at slot " + args[1 - offset], t); } // if (args[1 - offset] instanceof CNull) { // is = m.getItemInHand(); // } else { // int slot = Static.getInt32(args[1 - offset]); // is = m.getInventory().getItem(slot); // } CArray enchantArray = new CArray(t); if (!(args[2 - offset] instanceof CArray) && !(args[2 - offset] instanceof CNull)) { enchantArray.push(args[2 - offset], t); } else if (args[2 - offset] instanceof CNull) { for (MCEnchantment e : is.getEnchantments().keySet()) { is.removeEnchantment(e); } } else { enchantArray = (CArray) args[2 - offset]; } for (String key : enchantArray.stringKeySet()) { MCEnchantment e = StaticLayer.GetEnchantmentByName(Enchantments.ConvertName(enchantArray.get(key, t).val())); if (e == null) { throw new CREEnchantmentException(enchantArray.get(key, t).val().toUpperCase() + " is not a valid" + " enchantment type", t); } is.removeEnchantment(e); } return CVoid.VOID; } } @api(environments={CommandHelperEnvironment.class}) public static class get_enchant_inv extends AbstractFunction { @Override public String getName() { return "get_enchant_inv"; } @Override public Integer[] numArgs() { return new Integer[]{1, 2}; } @Override public String docs() { return "array {[player], slot} Returns an array of arrays of the enchantments and their levels on the given" + " item. For example: array(array(DAMAGE_ALL, DAMAGE_UNDEAD), array(1, 2))"; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CREPlayerOfflineException.class, CRECastException.class}; } @Override public boolean isRestricted() { return true; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { MCPlayer m = environment.getEnv(CommandHelperEnvironment.class).GetPlayer(); Construct slot; if (args.length == 2) { m = Static.GetPlayer(args[0].val(), t); slot = args[1]; } else { slot = args[0]; if (m == null) { throw new CREPlayerOfflineException("Invalid sender!", t); } } MCItemStack is = m.getItemAt(slot instanceof CNull ? null : Static.getInt32(slot, t)); if (is == null) { throw new CRECastException("There is no item at slot " + slot, t); } // if(slot instanceof CNull){ // is = m.getItemInHand(); // } else { // int slotID = Static.getInt32(slot); // is = m.getInventory().getItem(slotID); // } CArray enchants = new CArray(t); CArray levels = new CArray(t); for (Map.Entry<MCEnchantment, Integer> entry : is.getEnchantments().entrySet()) { MCEnchantment e = entry.getKey(); Integer l = entry.getValue(); enchants.push(new CString(e.getName(), t), t); levels.push(new CInt(l, t), t); } return new CArray(t, enchants, levels); } } @api public static class can_enchant_target extends AbstractFunction { @Override public String getName() { return "can_enchant_target"; } @Override public Integer[] numArgs() { return new Integer[]{2}; } @Override public String docs() { return "boolean {name, targetItem} Given an enchantment name, and target item id," + " returns wether or not that item can be enchanted with that enchantment." + " Throws an EnchantmentException if the name is not a valid enchantment" + " type."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CREEnchantmentException.class, CREFormatException.class}; } @Override public boolean isRestricted() { return false; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { try { String name = Enchantments.ConvertName(args[0].val()); MCEnchantment e = StaticLayer.GetEnchantmentByName(name); MCItemStack is = Static.ParseItemNotation(this.getName(), args[1].val(), 1, t); return CBoolean.get(e.canEnchantItem(is)); } catch (NullPointerException e) { throw new CREEnchantmentException(args[0].val().toUpperCase() + " is not a known enchantment type.", t); } } } @api public static class get_enchant_max extends AbstractFunction { @Override public String getName() { return "get_enchant_max"; } @Override public Integer[] numArgs() { return new Integer[]{1}; } @Override public String docs() { return "int {name} Given an enchantment name, returns the max level it can be." + " If name is not a valid enchantment, an EnchantException is thrown."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CREEnchantmentException.class}; } @Override public boolean isRestricted() { return false; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return null; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { try { String name = Enchantments.ConvertName(args[0].val()); MCEnchantment e = StaticLayer.GetEnchantmentByName(name); return new CInt(e.getMaxLevel(), t); } catch (NullPointerException e) { throw new CREEnchantmentException(args[0].val().toUpperCase() + " is not a known enchantment type.", t); } } } @api public static class get_enchants extends AbstractFunction { private static Map<String, CArray> cache = new HashMap<String, CArray>(); @Override public String getName() { return "get_enchants"; } @Override public Integer[] numArgs() { return new Integer[]{1}; } @Override public String docs() { return "array {item} Given an item id, returns the enchantments that can" + " be validly added to this item. This may return an empty array."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{CREFormatException.class}; } @Override public boolean isRestricted() { return false; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { MCItemStack is = Static.ParseItemNotation(this.getName(), args[0].val(), 1, t); /** * Because enchantment types won't change from run to run, we can * cache here, and save time on duplicate lookups. */ if (cache.containsKey(args[0].val())) { return cache.get(args[0].val()).clone(); } CArray ca = new CArray(t); for (MCEnchantment e : StaticLayer.GetEnchantmentValues()) { if (e.canEnchantItem(is)) { ca.push(new CString(e.getName(), t), t); } } cache.put(args[0].val(), ca); return ca.clone(); } } @api public static class is_enchantment extends AbstractFunction { @Override public String getName() { return "is_enchantment"; } @Override public Integer[] numArgs() { return new Integer[]{1}; } @Override public String docs() { return "boolean {name} Returns true if this name is a valid enchantment type. Note" + " that either the bukkit names or the wiki names are valid."; } @Override public Class<? extends CREThrowable>[] thrown() { return new Class[]{}; } @Override public boolean isRestricted() { return false; } @Override public CHVersion since() { return CHVersion.V3_3_0; } @Override public Boolean runAsync() { return false; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { try { MCEnchantment e = StaticLayer.GetEnchantmentByName(args[0].val()); return CBoolean.TRUE; } catch (NullPointerException e) { return CBoolean.FALSE; } } } @api public static class enchantment_list extends AbstractFunction{ @Override public Class<? extends CREThrowable>[] thrown() { return null; } @Override public boolean isRestricted() { return false; } @Override public Boolean runAsync() { return null; } @Override public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { MCEnchantment[] enchantments = StaticLayer.GetEnchantmentValues(); CArray ret = new CArray(t); for(MCEnchantment e : enchantments){ ret.push(new CString(e.getName(), t), t); } return ret; } @Override public String getName() { return "enchantment_list"; } @Override public Integer[] numArgs() { return new Integer[]{0}; } @Override public String docs() { return "array {} Returns an informational list of all valid enchantment names. Note that this will" + " simply cover all enchantment types, but may not be a comprehensive list of names that" + " can be accepted, there may be more, however, the list returned here is \"comprehensive\"" + " and \"official\". Additionally, this may vary from server type to server type."; } @Override public CHVersion since() { return CHVersion.V3_3_1; } } }