package org.moparscape.msc.config;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.moparscape.msc.gs.external.EntityHandler;
import org.moparscape.msc.gs.external.FiremakingDef;
import org.moparscape.msc.gs.external.GameObjectLoc;
import org.moparscape.msc.gs.external.ItemLoc;
import org.moparscape.msc.gs.external.NPCLoc;
import org.moparscape.msc.gs.external.ObjectFishDef;
import org.moparscape.msc.gs.external.ObjectMiningDef;
import org.moparscape.msc.gs.external.ObjectWoodcuttingDef;
import org.moparscape.msc.gs.external.SpellDef;
import org.moparscape.msc.gs.model.Entity;
import org.moparscape.msc.gs.model.GameObject;
import org.moparscape.msc.gs.model.InvItem;
import org.moparscape.msc.gs.model.Mob;
import org.moparscape.msc.gs.model.Npc;
import org.moparscape.msc.gs.model.Player;
import org.moparscape.msc.gs.model.Point;
import org.moparscape.msc.gs.model.Shop;
import org.moparscape.msc.gs.tools.DataConversions;


public class Formulae {
	public static final Point[] noremoveTiles = {new Point(341,487), new Point(343,581), new Point(92,649), new Point(434,682), new Point(660,551), new Point(196,3266), new Point(59,573), new Point(560,472), new Point(140,180), new Point(285,195), new Point(243,178), new Point(394,851), new Point(388,851), new Point(512,550)};
    public static final int[] arrowIDs = { 723, 647, 646, 645, 644, 643, 642, 641, 640, 639, 638, 574, 11 };
    public static final int[] bodySprites = { 2, 5 };
    public static final int[] boltIDs = { 786, 592, 190 };
    public static final int[] bowIDs = { 188, 189, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657 };
    // spell
    public static final int[] experienceArray = { 83, 174, 276, 388, 512, 650, 801, 969, 1154, 1358, 1584, 1833, 2107, 2411, 2746, 3115, 3523, 3973, 4470, 5018, 5624, 6291, 7028, 7842, 8740, 9730, 10824, 12031, 13363, 14833, 16456, 18247, 20224, 22406, 24815, 27473, 30408, 33648, 37224, 41171, 45529, 50339, 55649, 61512, 67983, 75127, 83014, 91721, 101333, 111945, 123660, 136594, 150872, 166636, 184040, 203254, 224466, 247886, 273742, 302288, 333804, 368599, 407015, 449428, 496254, 547953, 605032, 668051, 737627, 814445, 899257, 992895, 1096278, 1210421, 1336443, 1475581, 1629200, 1798808, 1986068, 2192818, 2421087, 2673114, 2951373, 3258594, 3597792, 3972294, 4385776, 4842295, 5346332, 5902831, 6517253, 7195629, 7944614, 8771558, 9684577, 10692629, 11805606, 13034431, 14391160 };
    public static final int[] headSprites = { 1, 4, 6, 7, 8 };
    public static final int[] miningAxeIDs = { 1262, 1261, 1260, 1259, 1258, 156 };
    public static final int[] miningAxeLvls = { 41, 31, 21, 6, 1, 1 };

    /**
     * Cubic P2P boundaries. MinX, MinY - MaxX, MaxY
     */
    public static final java.awt.Point[][] F2PWILD_LOCS = {{new java.awt.Point(48, 96), new java.awt.Point(335, 142)} };
    public static final java.awt.Point[][] P2P_LOCS = { { new java.awt.Point(436, 432), new java.awt.Point(719, 906) }, { new java.awt.Point(48, 96), new java.awt.Point(335, 142) }, { new java.awt.Point(343, 567), new java.awt.Point(457, 432) }, { new java.awt.Point(203, 3206), new java.awt.Point(233, 3265) }, { new java.awt.Point(397, 525), new java.awt.Point(441, 579), }, { new java.awt.Point(431, 0), new java.awt.Point(1007, 1007) } };
    public static final int[] potions1Dose = { 224, 476, 479, 482, 485, 488, 491, 494, 497, 500, 568, 571 };
    public static final int[] potions2Dose = { 223, 475, 478, 481, 484, 487, 490, 493, 496, 499, 567, 570 };
    public static final int[] potions3Dose = { 222, 474, 477, 480, 483, 486, 489, 492, 495, 498, 566, 569 };
    public static final int[] potionsUnfinished = { 454, 455, 456, 457, 458, 459, 460, 461, 462, 463 };

    private static Random r = new Random();
    public static final int[] runeIDs = { 31, 32, 33, 34, 35, 36, 37, 38, 40, 41, 42, 46, 619, 825 };
    public static final int[] safePacketIDs = { 70, 123, 128, 255 };
    public static final String[] statArray = { "attack", "defense", "strength", "hits", "ranged", "prayer", "magic", "cooking", "woodcut", "fletching", "fishing", "firemaking", "crafting", "smithing", "mining", "herblaw", "agility", "thieving" };

    public static final int[] woodcuttingAxeIDs = { 405, 204, 203, 428, 88, 12, 87 };

    public static final int[] xbowIDs = { 59, 60 };
    
    public static ArrayList<Point> dray2edge = new ArrayList<Point>();
    /**
     * Array of items that cannot be telegrabbed.
     */
	public static int[] telegrabBlocked = { 828, 831, 832, 1289, 422, 1315, 1314, 1316, 971 };
	
    static 
		{
		dray2edge.add(new Point(114,638));
		dray2edge.add(new Point(120,621));
		dray2edge.add(new Point(131,612));
		dray2edge.add(new Point(159,596));
		dray2edge.add(new Point(195,583));
		dray2edge.add(new Point(201,562));
		dray2edge.add(new Point(200,540));
		dray2edge.add(new Point(227,501));
		dray2edge.add(new Point(225,460));
		dray2edge.add(new Point(206,448));
		}

    /**
     * Adds the prayers together to calculate what perecntage the stat should be
     * increased
     */
    public static double addPrayers(boolean first, boolean second, boolean third) {
	if (third) {
	    return 1.15D;
	}
	if (second) {
	    return 1.1D;
	}
	if (first) {
	    return 1.05D;
	}
	return 1.0D;
    }

    /**
     * Returns a power to assosiate with each arrow
     */
    private static double arrowPower(int arrowID) {
	switch (arrowID) {
	case 11: // bronze arrows
	case 574: // poison bronze arrows
	case 190: // crossbow bolts
	case 592: // poison cross bow bolts
	case 1013: // bronze throwing dart
	case 1122: // poison bronze throwing dart
	    return 0;
	case 638:// iron arrows
	case 639:// poison iron arrows
	case 1015: // iron throwing dart
	case 1123:// poison iron throwing dart
	    return 0.5;
	case 640:// steel arrows
	case 641:// poison steel arrows
	case 1024: // steel throwing dart
	case 1124: // poison steel throwing dart
	case 1076:// bronze throwing dart
	case 1128:// poison bronze throwing knife
	case 827:// bronze spear
	case 1135:// poison bronze spear
	    return 1;
	case 642:// mith arrows
	case 643:// poison mith arrows
	case 786:// pearle crossbow bolts
	case 1068:// mith throwing dart
	case 1125: // poison mith throwing dart
	case 1075:// iron throwing dart
	case 1129:// poison iron throwing knife
	case 1088:// iron spear
	case 1136:// poison iron spear
	    return 1.5;
	case 644:// addy arrows
	case 645:// poison addy arrows
	case 1069:// addy throwing dart
	case 1126:// poison addy throwing dart
	case 1077:// steel throwing knife
	case 1130:// poison steel throwing knife
	case 1089:// steel spear
	case 1137:// poison steel spear
	    return 1.75;
	case 1081:// black throwing knife
	case 1132:// poison black throwing knife
	    return 2;
	case 646:// rune arrows
	case 647:// poison rune arrows
	case 1070:// rune throwing dart
	case 1127:// poison rune throwing dart
	case 1078:// mith throwing knife
	case 1131:// poison mith throwing knife
	case 1090:// mith spear
	case 1138:// poison mith spear
	    return 5;
	case 723:// ice arrows
	case 1079:// addy throwing knife
	case 1133:// poison addy throwing knife
	case 1091:// addy spear
	case 1139:// poison addy spear
	    return 6;
	case 1080:// rune throwing knife
	case 1134:// poison rune throwing knife
	case 1092:// rune spear
	case 1140:// poison rune spear
	    return 7;
	case 785:// lit arrow (not stackable, why not?)
	    return 10;
	default:
	    return 0;
	}
    }

    public static int bitToDoorDir(int bit) {
	switch (bit) {
	case 1:
	    return 0;
	case 2:
	    return 1;
	case 4:
	    return -1;
	case 8:
	    return -1;
	}
	return -1;
    }

    public static int bitToObjectDir(int bit) {
	switch (bit) {
	case 1:
	    return 6;
	case 2:
	    return 0;
	case 4:
	    return 2;
	case 8:
	    return 4;
	}
	return -1;
    }

    /**
     * Decide if the food we are cooking should be burned or not
     */
    public static boolean burnFood(int foodId, int cookingLevel) {
	int levelDiff = cookingLevel - EntityHandler.getItemCookingDef(foodId).getReqLevel();
	if (levelDiff < 0) {
	    return true;
	}
	if (levelDiff >= 20) {
	    return false;
	}
	return DataConversions.random(0, levelDiff + 1) == 0;
    }

    /**
     * Calulates what one mob should hit on another with meelee
     */
    public static int calcFightHit(Mob attacker, Mob defender) {
	int max = maxHit(attacker.getStrength(), attacker.getWeaponPowerPoints(), attacker.isPrayerActivated(1), attacker.isPrayerActivated(4), attacker.isPrayerActivated(10), styleBonus(attacker, 2));
	int newAtt = (int) 
		(
			addPrayers(attacker.isPrayerActivated(2), attacker.isPrayerActivated(5), attacker.isPrayerActivated(11)
		) 
		* 
			(attacker.getAttack() / 0.8D) 
		+ 
		(
				(DataConversions.random(0, 4) == 0 ? attacker.getWeaponPowerPoints() : attacker.getWeaponAimPoints()) / 2.5D
		) 
		+ 
		(
				attacker.getCombatStyle() == 1 && DataConversions.random(0, 2) == 0 ? 4 : 0
		)
		+
		(
				DataConversions.random(0, 100) <= 10 ? (attacker.getStrength() / 5D) : 0
		)
		+ 
		(
				styleBonus(attacker, 0) * 2)
		);
	int newDef = (int) 
		(
			addPrayers(defender.isPrayerActivated(0), defender.isPrayerActivated(3), defender.isPrayerActivated(9)) 
				* 
			(
					(DataConversions.random(0, 100) <= 5 ? 0 : defender.getDefense()) * 1.1D
			) 
				+ 
			(
					(DataConversions.random(0, 100) <= 5 ? 0 : defender.getArmourPoints()) / 2.75D
			) 
				+ 
			(defender.getStrength() / 4D) 
				+ 
			(styleBonus(defender, 1) * 2)
		);

	int hitChance = DataConversions.random(0, 100) + (newAtt - newDef);
	if (attacker instanceof Npc) {
	    hitChance -= 5;
	}
	if (DataConversions.random(0, 100) <= 10) {
	    hitChance += 20;
	}
	if (hitChance > (defender instanceof Npc ? 40 : 50)) {
	    int maxProb = 5; // 5%
	    int nearMaxProb = 7; // 7%
	    int avProb = 73; // 73%
	    int lowHit = 10; // 15%

	    // Probablities are shifted up/down based on armour
	    int shiftValue = (int) Math.round(defender.getArmourPoints() * 0.02D);
	    maxProb -= shiftValue;
	    nearMaxProb -= (int) Math.round(shiftValue * 1.5);
	    avProb -= (int) Math.round(shiftValue * 2.0);
	    lowHit += (int) Math.round(shiftValue * 3.5);

	    int hitRange = DataConversions.random(0, 100);

	    if (hitRange >= (100 - maxProb)) {
		return max;
	    } else if (hitRange >= (100 - nearMaxProb)) {
		return DataConversions.roundUp(Math.abs((max - (max * (DataConversions.random(0, 10) * 0.01D)))));
	    } else if (hitRange >= (100 - avProb)) {
		int newMax = (int) DataConversions.roundUp((max - (max * 0.1D)));
		return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 50) * 0.01D)))));
	    } else {
		int newMax = (int) DataConversions.roundUp((max - (max * 0.5D)));
		return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 95) * 0.01D)))));
	    }
	}
	return 0;
    }

    public static int calcFightHitWithNPC(Mob attacker, Mob defender) {

	int max = maxHit(attacker.getStrength(), attacker.getWeaponPowerPoints(), attacker.isPrayerActivated(1), attacker.isPrayerActivated(4), attacker.isPrayerActivated(10), styleBonus(attacker, 2));
	if (attacker instanceof Npc) {
	    Npc n = (Npc) attacker;
	    if (n.getID() == 3) // Chickens only doing 1 damage.
		max = 1;
	}

	//int newAtt = (int) (addPrayers(attacker.isPrayerActivated(2), attacker.isPrayerActivated(5), attacker.isPrayerActivated(11)) * (attacker.getAttack() / 0.7D) + ((DataConversions.random(0, 4) == 0 ? attacker.getWeaponPowerPoints() : attacker.getWeaponAimPoints()) / 3D) + (attacker.getCombatStyle() == 1 && DataConversions.random(0, 2) == 0 ? 4 : 0) + (styleBonus(attacker, 0) * 2));

        int newAtt = (int) (addPrayers(attacker.isPrayerActivated(2), attacker.isPrayerActivated(5), attacker.isPrayerActivated(11))
        * (attacker.getAttack())
        + ((DataConversions.random(0, 4) == 0 ? attacker.getWeaponPowerPoints() : attacker.getWeaponAimPoints()) / 3D)
        + (attacker.getCombatStyle() == 1 && DataConversions.random(0, 2) == 0 ? 4 : 0)
        + (styleBonus(attacker, 0) * 2));

	int newDef = (int) (addPrayers(defender.isPrayerActivated(0), defender.isPrayerActivated(3), defender.isPrayerActivated(9)) * defender.getDefense() + (defender.getArmourPoints() / 4D) + (defender.getStrength() / 4D) + (styleBonus(defender, 1) * 2));

	/*
	 * if(defender instanceof Player) { if(defender.getCombatLevel() < 70 &&
	 * defender.getCombatLevel() > 45) newDef = newDef + (int)(newDef *
	 * 0.25); else if(defender.getCombatLevel() > 25 &&
	 * defender.getCombatLevel() < 45) newDef = newDef + (int)(newDef *
	 * 0.35);
	 * 
	 * else if(defender.getCombatLevel() > 8 && defender.getCombatLevel() <
	 * 25) newDef = newDef + (int)(newDef * 0.45);
	 * 
	 * else if(defender.getCombatLevel() > 1 && defender.getCombatLevel() <
	 * 8) newDef = newDef + (int)(newDef * 0.55); }
	 */
	if (attacker instanceof Player) {
	    //newDef += newDef / 8;
	    newDef -= newDef / 8;
	}

	int hitChance = DataConversions.random(0, 100) + (newAtt - newDef);
	//Added this
        if (attacker instanceof Player)
           hitChance += (int)(DataConversions.random(0, attacker.getAttack()) + 1) / 1.33;

	if (attacker instanceof Npc) {
	    hitChance -= 5;
	}
	if (hitChance > (defender instanceof Npc ? 40 : 50)) {
	    int maxProb = 5; // 5%
	    int nearMaxProb = 10; // 10%
	    int avProb = 80; // 70%
	    int lowHit = 10; // 15%

	    // Probablities are shifted up/down based on armour
	    int shiftValue = (int) Math.round(defender.getArmourPoints() * 0.02D);
	    maxProb -= shiftValue;
	    nearMaxProb -= (int) Math.round(shiftValue * 1.5);
	    avProb -= (int) Math.round(shiftValue * 2.0);
	    lowHit += (int) Math.round(shiftValue * 3.5);

	    int hitRange = DataConversions.random(0, 100);

	    if (hitRange >= (100 - maxProb)) {
		return max;
	    } else if (hitRange >= (100 - nearMaxProb)) {
		return DataConversions.roundUp(Math.abs((max - (max * (DataConversions.random(0, 10) * 0.01D)))));
	    } else if (hitRange >= (100 - avProb)) {
		int newMax = (int) DataConversions.roundUp((max - (max * 0.1D)));
		return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 50) * 0.01D)))));
	    } else {
		int newMax = (int) DataConversions.roundUp((max - (max * 0.5D)));
		return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 95) * 0.01D)))));
	    }
	}
	return 0;
    }

    public static int calcGodSpells(Mob attacker, Mob defender) {
	if (attacker instanceof Player) {
	    Player owner = (Player) attacker;
	    int newAtt = (int) ((owner.getMagicPoints()) + owner.getCurStat(6));
	    int newDef = (int) ((addPrayers(defender.isPrayerActivated(0), defender.isPrayerActivated(3), defender.isPrayerActivated(9)) * defender.getDefense() / 4D) + (defender.getArmourPoints() / 4D));
	    int hitChance = DataConversions.random(0, 150 + (newAtt - newDef));
	    // int hitChance = (int)(50D + (double)owner.getMagicPoints() -
	    // newDef);

	    if (hitChance > (defender instanceof Npc ? 50 : 60)) {
		// int max = owner.isCharged() ? Rand(15, 25) : Rand(0, 10);
		int max;
		if (owner.isCharged()) {
		    max = Rand(14, 25);
		    
		} else {
		    max = Rand(0, 10);
		}
		int maxProb = 5; // 5%
		int nearMaxProb = 10; // 10%
		int avProb = 80; // 80%
		int lowHit = 5; // 5%

		// Probablities are shifted up/down based on armour
		int shiftValue = (int) Math.round(defender.getArmourPoints() * 0.02D);
		maxProb -= shiftValue;
		nearMaxProb -= (int) Math.round(shiftValue * 1.5);
		avProb -= (int) Math.round(shiftValue * 2.0);
		lowHit += (int) Math.round(shiftValue * 3.5);

		int hitRange = DataConversions.random(0, 100);

		if (hitRange >= (100 - maxProb)) {
		    return max;
		} else if (hitRange >= (100 - nearMaxProb)) {
		    return DataConversions.roundUp(Math.abs((max - (max * (DataConversions.random(0, 10) * 0.01D)))));
		} else if (hitRange >= (100 - avProb)) {
		    int newMax = (int) DataConversions.roundUp((max - (max * 0.1D)));
		    return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 50) * 0.01D)))));
		} else {
		    int newMax = (int) DataConversions.roundUp((max - (max * 0.5D)));
		    return DataConversions.roundUp(Math.abs((newMax - (newMax * (DataConversions.random(0, 95) * 0.01D)))));
		}
	    }
	}
	return 0;
    }

    /**
     * Calculates what one mob should hit on another with range
     */
    public static int calcRangeHit(int rangeLvl, int rangeEquip, int armourEquip, int arrowID) {
	int armourRatio = (int) (60D + ((double) ((rangeEquip * 3D) - armourEquip) / 300D) * 40D);

	if (DataConversions.random(0, 100) > armourRatio && DataConversions.random(0, 1) == 0) {
	    return 0;
	}

	int max = (int) (((double) rangeLvl * 0.15D) + 0.85D + arrowPower(arrowID));
	int peak = (int) (((double) max / 100D) * (double) armourRatio);
	int dip = (int) (((double) peak / 3D) * 2D);
	return DataConversions.randomWeighted(0, dip, peak, max);
    }

    /**
     * Calculates what a spell should hit based on its strength and the magic
     * equipment stats of the caster
     */
    public static int calcSpellHit(int spellStr, int magicEquip) {
	int mageRatio = (int) (45D + (double) magicEquip);
	int max = spellStr;
	int peak = (int) (((double) spellStr / 100D) * (double) mageRatio);
	int dip = (int) ((peak / 3D) * 2D);
	return DataConversions.randomWeighted(0, dip, peak, max);
    }

    /**
     * Should the spell cast or fail?
     */
    public static boolean castSpell(SpellDef def, int magicLevel, int magicEquip) {
	int levelDiff = magicLevel - def.getReqLevel();

	if (magicEquip >= 30 && levelDiff >= 5)
	    return true;
	if (magicEquip >= 25 && levelDiff >= 6)
	    return true;
	if (magicEquip >= 20 && levelDiff >= 7)
	    return true;
	if (magicEquip >= 15 && levelDiff >= 8)
	    return true;
	if (magicEquip >= 10 && levelDiff >= 9)
	    return true;
	if (levelDiff < 0) {
	    return false;
	}
	if (levelDiff >= 10) {
	    return true;
	}
	return DataConversions.random(0, (levelDiff + 2) * 2) != 0;
    }

    /**
     * Calculate how much experience a Mob gives
     */
    public static int combatExperience(Mob mob) {
	double exp = ((mob.getCombatLevel() * 2) + 10) * 1.5D;
	return (int) (mob instanceof Player ? (exp / 4D) : exp);
    }

    /*
     * Should the pot crack?
     */
    public static boolean crackPot(int requiredLvl, int craftingLvl) {
	int levelDiff = craftingLvl - requiredLvl;
	if (levelDiff < 0) {
	    return true;
	}
	if (levelDiff >= 20) {
	    return false;
	}
	return DataConversions.random(0, levelDiff + 1) == 0;
    }

    /**
     * Should the web be cut?
     */
    public static boolean cutWeb() {
	return DataConversions.random(0, 4) != 0;
    }

    public static boolean doorAtFacing(Entity e, int x, int y, int dir) {
	if (dir >= 0 && e instanceof GameObject) {
	    GameObject obj = (GameObject) e;
	    return obj.getType() == 1 && obj.getDirection() == dir && obj.isOn(x, y);
	}
	return false;
    }

    /**
     * Check what level the given experience corresponds to
     */
    public static int experienceToLevel(int exp) {
	for (int level = 0; level < 98; level++) {
	    if (exp >= experienceArray[level]) {
		continue;
	    }
	    return (level + 1);
	}
	return 99;
    }

    /**
     * Decide if we fall off the obstacle or not
     */
    public static int failObstacle(Player player, int reqLvl) {
	int levelDiff = player.getCurStat(16) - reqLvl;
	if (levelDiff < 0)
	    return 1;

	if (levelDiff >= (reqLvl * 2))
	    return -1;

	if (DataConversions.random(0, levelDiff + 1) == 0)
	    return DataConversions.roundUp(player.getMaxStat(3) * DataConversions.random(0.02, 0.04));
	else
	    return -1;
    }

    public static int firemakingExp(int level, int baseExp) {
	return DataConversions.roundUp(baseExp + (level * 1.75D));
    }

    /**
     * Generates a session id
     */
    public static long generateSessionKey(byte userByte) {
    	return DataConversions.getRandom().nextLong();
    }

    /**
     * Gets the type of bar we have
     */
    public static int getBarType(int barID) {
	switch (barID) {
		case 169:
		    return 0;
		case 170:
		    return 1;
		case 171:
		    return 2;
		case 173:
		    return 3;
		case 174:
		    return 4;
		case 408:
		    return 5;
		}
		return -1;
    }

    /**
     * Calculate a mobs combat level based on their stats
     */
    public static int getCombatlevel(int[] stats) {
	return getCombatLevel(stats[0], stats[1], stats[2], stats[3], stats[6], stats[5], stats[4]);
    }

    /**
     * Calculate a mobs combat level based on their stats
     */
    public static int getCombatLevel(int att, int def, int str, int hits, int magic, int pray, int range) {
	double attack = att + str;
	double defense = def + hits;
	double mage = pray + magic;
	mage /= 8D;

	if (attack < ((double) range * 1.5D)) {
	    return (int) ((defense / 4D) + ((double) range * 0.375D) + mage);
	} else {
	    return (int) ((attack / 4D) + (defense / 4D) + mage);
	}
    }

    /**
     * gets the new sprite direction to face
     * 
     */
    public static int getDirection(Mob you, Mob them) {

	if (you.getX() == them.getX() + 1 && you.getY() == them.getY() + 1) // bottom
	    // left
	    return 3;
	else if (you.getX() == them.getX() + 1 && you.getY() == them.getY() - 1) // top
	    // left
	    return 1;
	else if (you.getX() == them.getX() - 1 && you.getY() == them.getY() - 1) // right
	    // up
	    return 7;
	else if (you.getX() == them.getX() - 1 && you.getY() == them.getY() + 1) // right/down
	    return 5;
	else if (you.getX() == them.getX() - 1) // face right
	    return 6;
	else if (you.getX() == them.getX() + 1) // face left
	    return 2;
	else if (you.getY() == them.getY() + 1) // face down
	    return 4;
	else if (you.getY() == them.getY() - 1) // face up
	    return 0;

	return -1;
    }

    /**
     * Gets the empty jug ID
     */
    public static int getEmptyJug(int fullJug) {
	switch (fullJug) {
	case 50:
	    return 21;
	case 141:
	    return 140;
	case 342:
	    return 341;
	}
	return -1;
    }

    /**
     * Decide what fish, if any, we should get from the water
     */
    public static ObjectFishDef getFish(int waterId, int fishingLevel, int click) {
	ArrayList<ObjectFishDef> fish = new ArrayList<ObjectFishDef>();
	for (ObjectFishDef def : EntityHandler.getObjectFishingDef(waterId, click).getFishDefs()) {
	    if (fishingLevel >= def.getReqLevel()) {
		fish.add(def);
	    }
	}
	if (fish.size() <= 0) {
	    return null;
	}
	ObjectFishDef thisFish = fish.get(DataConversions.random(0, fish.size() - 1));
	int levelDiff = fishingLevel - thisFish.getReqLevel();
	if (levelDiff < 0) {
	    return null;
	}
	return DataConversions.percentChance(offsetToPercent(levelDiff)) ? thisFish : null;
    }

    /**
     * Returns a gem ID
     */
    public static int getGem() {
	int rand = DataConversions.random(0, 100);
	if (rand < 10) {
	    return 157;
	} else if (rand < 30) {
	    return 158;
	} else if (rand < 60) {
	    return 159;
	} else {
	    return 160;
	}
    }

    /**
     * Check what height we are currently at on the map
     */
    public static int getHeight(int y) {
	return (int) (y / 944);
    }

    /**
     * Check what height we are currently at on the map
     */
    public static int getHeight(Point location) {
	return getHeight(location.getY());
    }

    public static int getItemPos(Shop shop, int id) {
	for (int i = 0; i < shop.getItems().size(); i++) {
	    if (shop.getItems().get(i).getID() == id)
		return i;
	}
	return -1;
    }

    public static List<InvItem> getKeyChestLoot() {
	List<InvItem>[] possibleLoots = (List<InvItem>[]) EntityHandler.getKeyChestLoots();
	return possibleLoots[DataConversions.random(0, possibleLoots.length - 1)];
    }

    /**
     * Should we get a log from the tree?
     */
    public static boolean getLog(ObjectWoodcuttingDef def, int woodcutLevel, int axeId) {
	int levelDiff = woodcutLevel - def.getReqLevel();
	if (levelDiff < 0) {
	    return false;
	}
	switch (axeId) {
	case 87:
	    levelDiff += 0;
	    break;
	case 12:
	    levelDiff += 2;
	    break;
	case 428:
	    levelDiff += 4;
	    break;
	case 88:
	    levelDiff += 6;
	    break;
	case 203:
	    levelDiff += 8;
	    break;
	case 204:
	    levelDiff += 10;
	    break;
	case 405:
	    levelDiff += 12;
	    break;
	}
	if (def.getReqLevel() == 1 && levelDiff >= 40) {
	    return true;
	}
	return DataConversions.percentChance(offsetToPercent(levelDiff));
    }

    /*
     * public static int calcFightHitWithNPC(Mob attacker, Mob defender) { int
     * newAtt = (int)((addPrayers(attacker.isPrayerActivated(2),
     * attacker.isPrayerActivated(5), attacker.isPrayerActivated(11)) *
     * attacker.getAttack()) + (attacker.getWeaponAimPoints() / 4D) +
     * styleBonus(attacker, 0)); int newDef =
     * (int)((addPrayers(defender.isPrayerActivated(0),
     * defender.isPrayerActivated(3), defender.isPrayerActivated(9)) *
     * defender.getDefense()) + (defender.getArmourPoints() / 4D) +
     * styleBonus(attacker, 1));
     * 
     * int hitChance = DataConversions.random(0, 100) + (newAtt - newDef);
     * 
     * if(hitChance > (defender instanceof Npc ? 50 : 60)) { int max =
     * maxHit(attacker.getStrength(), attacker.getWeaponPowerPoints(),
     * attacker.isPrayerActivated(1), attacker.isPrayerActivated(4),
     * attacker.isPrayerActivated(10), styleBonus(attacker, 2));
     * 
     * int maxProb = 5; // 5% int nearMaxProb = 10; // 10% int avProb = 80; //
     * 80% int lowHit = 5; // 5%
     * 
     * // Probablities are shifted up/down based on armour int shiftValue =
     * (int)Math.round(defender.getArmourPoints() * 0.02D); maxProb -=
     * shiftValue; nearMaxProb -= (int)Math.round(shiftValue * 1.5); avProb -=
     * (int)Math.round(shiftValue * 2.0); lowHit += (int)Math.round(shiftValue *
     * 3.5);
     * 
     * int hitRange = DataConversions.random(0, 100);
     * 
     * if(hitRange >= (100 - maxProb)) { return max; } else if(hitRange >= (100
     * - nearMaxProb)) { return DataConversions.roundUp(Math.abs((max - (max *
     * (DataConversions.random(0, 10) * 0.01D))))); } else if(hitRange >= (100 -
     * avProb)) { int newMax = (int)DataConversions.roundUp((max - (max *
     * 0.1D))); return DataConversions.roundUp(Math.abs((newMax - (newMax *
     * (DataConversions.random(0, 50) * 0.01D))))); } else { int newMax =
     * (int)DataConversions.roundUp((max - (max * 0.5D))); return
     * DataConversions.roundUp(Math.abs((newMax - (newMax *
     * (DataConversions.random(0, 95) * 0.01D))))); } } return 0; }
     */

    public static String getLvlDiffColour(int lvlDiff) {
	if (lvlDiff < -9) {
	    return "@red@";
	} else if (lvlDiff < -6) {
	    return "@or3@";
	} else if (lvlDiff < -3) {
	    return "@or2@";
	} else if (lvlDiff < 0) {
	    return "@or1@";
	} else if (lvlDiff > 9) {
	    return "@gre@";
	} else if (lvlDiff > 6) {
	    return "@gr3@";
	} else if (lvlDiff > 3) {
	    return "@gr2@";
	} else if (lvlDiff > 0) {
	    return "@gr1@";
	}
	return "@whi@";
    }

    public static int getNewY(int currentY, boolean up) {
	int height = getHeight(currentY);
	int newHeight;
	if (up) {
	    if (height == 3) {
		newHeight = 0;
	    } else if (height >= 2) {
		return currentY;
	    } else {
		newHeight = height + 1;
	    }
	} else {
	    if (height == 0) {
		newHeight = 3;
	    } else if (height >= 3) {
		return currentY;
	    } else {
		newHeight = height - 1;
	    }
	}
	return (newHeight * 944) + (currentY % 944);
    }

    /**
     * Should we can get an ore from the rock?
     */
    public static boolean getOre(ObjectMiningDef def, int miningLevel, int axeId) {

	int levelDiff = miningLevel - def.getReqLevel();
	if (levelDiff > 50)
	    return Formulae.Rand(0, 9) != 1;
	if (levelDiff < 0) {
	    return false;
	}
	int bonus = 0;
	switch (axeId) {
	case 156:
	    bonus = 0;
	    break;
	case 1258:
	    bonus = 2;
	    break;
	case 1259:
	    bonus = 6;
	    break;
	case 1260:
	    bonus = 8;
	    break;
	case 1261:
	    bonus = 10;
	    break;
	case 1262:
	    bonus = 12;
	    break;
	}
	return DataConversions.percentChance(offsetToPercent(levelDiff + bonus));
    }

    public static int getPotionDose(int id) {
	if (DataConversions.inArray(potions1Dose, id)) {
	    return 1;
	}
	if (DataConversions.inArray(potions2Dose, id)) {
	    return 2;
	}
	if (DataConversions.inArray(potions3Dose, id)) {
	    return 3;
	}
	return 0;
    }

    /**
     * @author xEnt
     * 
     *         This method will calculate the new price for all items in the
     *         shop there are 3 different types of calculations. 1. Buy price -
     *         if the item is sold in the shop by default, use the base quantity
     *         for the calculations in the new price.
     * 
     *         2. Buy price - if a player has sold an item to a general store,
     *         leaving it to have no base quantity and player driven, we perform
     *         some secondary calculations.
     * 
     *         3. Sell price (this works like the second buy price, calculates
     *         depending on a static number of stock and does not use any type
     *         of base quantity calculations, may do a second one in future.
     * 
     * @param i
     *            - the selected Shop item, represented as an InvItem
     * @param shop
     *            - the shop model object
     * @param buy
     *            - if the item is for sale, or being sold
     * @return the item's calculated price of value
     */
    public static int getPrice(InvItem i, Shop shop, boolean buy) {
	if (buy)
	    return i.getDef().getBasePrice();
	else
	    return i.getDef().getBasePrice() / 2;
	/*
	 * final double GENERAL_STORE_BUY_MODIFIER = 0.685;
	 * 
	 * int newPrice = -1; // the newly given price. boolean playerSoldItem =
	 * false; // If true, there is no base quantity (a // player has sold
	 * this item to general // store) int curAmount = i.getAmount(); //
	 * current quantity the selected item has int maxStockAmount =
	 * shop.getEquilibrium(i.getID()); // the base // quantity of // the
	 * selected // shop item (if // has one)
	 * 
	 * if (maxStockAmount == 0 && shop.isGeneral()) playerSoldItem = true;
	 * // This item is an item that has no base // quantity, was sold by a
	 * player in general // store
	 * 
	 * if (buy) { // Decide if this item is being brought if (maxStockAmount
	 * == 0 && !shop.isGeneral()) // this should not // happen return
	 * 999999999; // rofl error price if (playerSoldItem) { // General
	 * store, no maximum quantity for a // player sold item. (requires
	 * different // calculations) // cost 15% more buying a 3rd party item
	 * from a general store int basePrice = shop.isGeneral() ?
	 * i.getDef().basePrice + (int) (i.getDef().basePrice * 0.15) :
	 * i.getDef().basePrice; if (basePrice > 10000) // forget any items that
	 * are worth past // 10k return basePrice; if (curAmount > 28) // after
	 * 28 quantity from a player sold // item, stick to a static price
	 * newPrice = basePrice - (int) (basePrice *
	 * GENERAL_STORE_BUY_MODIFIER); else { // do calculations to decide a
	 * price depending on the // quantity newPrice = basePrice - (int)
	 * (curAmount * 5); if (newPrice < basePrice - (int) (basePrice *
	 * GENERAL_STORE_BUY_MODIFIER)) newPrice = basePrice - (int) (basePrice
	 * * GENERAL_STORE_BUY_MODIFIER); } } else { // Has a base quantity if
	 * (curAmount >= maxStockAmount) // leave base price is full // stock is
	 * avaliable newPrice = i.getDef().basePrice; else // 75-100% quantity
	 * is in stock if (curAmount > maxStockAmount * 0.75 && curAmount <
	 * maxStockAmount) newPrice = i.getDef().basePrice + (int)
	 * (i.getDef().basePrice * getShopPercentage( i, 0, true)); else //
	 * 50-75% quantity is in stock if (curAmount > maxStockAmount * 0.50 &&
	 * curAmount <= maxStockAmount * 0.75) newPrice = i.getDef().basePrice +
	 * (int) (i.getDef().basePrice * getShopPercentage( i, 1, true)); else
	 * // 25-50% quantity is in stock if (curAmount > maxStockAmount * 0.25
	 * && curAmount <= maxStockAmount * 0.50) newPrice =
	 * i.getDef().basePrice + (int) (i.getDef().basePrice *
	 * getShopPercentage( i, 2, true)); else // 0-25% quantity is in stock
	 * if (curAmount > maxStockAmount * 0.0 && curAmount <= maxStockAmount *
	 * 0.25) newPrice = i.getDef().basePrice + (int) (i.getDef().basePrice *
	 * getShopPercentage( i, 3, true)); } if (newPrice == -1) return
	 * 99999999; // error? return newPrice; } else { // Sell if (i.getID()
	 * == 117) Logging.debug("Current stock: " + curAmount); int base =
	 * i.getDef().basePrice - (int) (i.getDef().basePrice / 2.5); // Sell
	 * price is 125% // lower than base // price to begin // with if
	 * (shop.isGeneral()) // 3rd party item (player sold) base = base -
	 * (int) (base * 0.10); // 10% less value, if general // store.
	 * 
	 * if (curAmount < 1) return base;
	 * 
	 * int price; // new price if (curAmount > 12) { price = base - (int)
	 * (base * 0.75); // 75% loss (believe it or // not, thats how it was) }
	 * else { price = base;
	 * 
	 * if (curAmount > 1) price = base - (int) (curAmount * (base * 0.045));
	 * if (price < base - (int) (base * 0.75)) base = base - (int) (base *
	 * 0.75); } if (price < 1) // should not happen return 0; // error price
	 * else return price; }
	 */
    }

    public static int getRangeDirection(Mob you, Mob them) {
	if (you.getX() > them.getX() && you.getY() == them.getY()) // face right
	    return 6;
	else if (you.getX() < them.getX() && you.getY() == them.getY()) // face
	    // left
	    return 2;
	else if (you.getY() < them.getY() && you.getX() == them.getX()) // face
	    // down
	    return 4;
	else if (you.getY() > them.getY() && you.getX() == them.getX()) // face
	    // up
	    return 0;
	else if (you.getX() <= them.getX() && you.getY() <= them.getY()) // bottom
	    // left
	    return 3;
	else if (you.getX() <= them.getX() && you.getY() >= them.getY()) // top
	    // left
	    return 1;
	else if (you.getX() >= them.getX() && you.getY() >= them.getY()) // right
	    // up
	    return 7;
	else if (you.getX() >= them.getX() && you.getY() <= them.getY()) // right/down
	    return 5;

	return -1;
    }

    // This is needed for the above method
    public static double getShopPercentage(InvItem item, int pos, boolean buy) {
	int[] prices = { 0, 10, 100, 1000, 10000 }; // base prices of the items.
	double[][] percentages = { { 0.5, 1.0, 1.5, 2.0 }, // in between 0 and
		// 10GP.
		{ 0.1, 0.3, 0.45, 0.6 }, // 10-100gp
		{ 0.1, 0.2, 0.3, 0.4 }, // 100-1000gp
		{ 0.5, 0.1, 0.15, 0.2 }, // 1000-10000gp
	};
	int key = -1;
	for (int i = 0; i < prices.length - 1; i++)
	    if (item.getDef().basePrice > prices[i] && item.getDef().basePrice < prices[i + 1])
		key = i;
	if (key == -1)
	    return -1;
	return percentages[key][pos];
    }

    /**
     * Gets the smithing exp for the given amount of the right bars
     */
    public static int getSmithingExp(int barID, int barCount) {
	int[] exps = { 13, 25, 37, 50, 83, 74 };
	int type = getBarType(barID);
	if (type < 0) {
	    return 0;
	}
	return exps[type] * barCount;
    }

    public static int getStat(String stat) {
	for (int i = 0; i < statArray.length; i++) {
	    if (statArray[i].equalsIgnoreCase(stat))
		return i;
	}

	return -1;
    }

    /**
     * Given a stat string get its index returns -1 on failure
     */
    public static int getStatIndex(String stat) {
	for (int index = 0; index < statArray.length; index++) {
	    if (stat.equalsIgnoreCase(statArray[index])) {
		return index;
	    }
	}
	return -1;
    }

    public static boolean isP2P(Object... objs) {
	return isP2P(false, objs);
    }

    /**
     * Performs coordinate checks on the locations in the P2P_LOCS array to
     * decide whether items/object/npc's/general x,y coordinates are in P2P area
     * 
     * @param objs
     *            - ItemLoc, GameObjectLoc, NPCLoc or an Integer.
     * @return - true if inside P2P area, otherwise false.
     */
    public static boolean isP2P(boolean f2pwildy, Object... objs) {
	int x = -1;
	int y = -1;
	if (objs.length == 1) {
	    Object obj = objs[0];
	    if (obj instanceof GameObjectLoc) {
		x = ((GameObjectLoc) obj).x;
		y = ((GameObjectLoc) obj).y;
	    } else if ((obj instanceof ItemLoc)) {
		x = ((ItemLoc) obj).x;
		y = ((ItemLoc) obj).y;
	    } else if (obj instanceof NPCLoc) {
		x = ((NPCLoc) obj).startX;
		y = ((NPCLoc) obj).startY;
	    }
	} else {
	    if (objs[0] instanceof Integer && objs[1] instanceof Integer) {
		x = (Integer) objs[0];
		y = (Integer) objs[1];
	    }
	}
	
	if (x == -1)
	    return false;
	if(!f2pwildy) {
	    for (int i = 0; i < P2P_LOCS.length; i++) {
		for (int ele = 0; ele < 4; ele++) {
		    if (x >= P2P_LOCS[i][0].getX() && x <= P2P_LOCS[i][1].getX() && y >= P2P_LOCS[i][0].getY() + ((ele) * 944) && y <= P2P_LOCS[i][1].getY() + ((ele) * 944))
			return true;
		}
	    }
	} else {
	    for (int i = 0; i < F2PWILD_LOCS.length; i++) {
		for (int ele = 0; ele < 4; ele++) {
		    if (x >= F2PWILD_LOCS[i][0].getX() && x <= F2PWILD_LOCS[i][1].getX() && y >= F2PWILD_LOCS[i][0].getY() + ((ele) * 944) && y <= F2PWILD_LOCS[i][1].getY() + ((ele) * 944))
			return true;
		}
	    } 
	}
	return false;
    }

    public static int levelToExperience(int level) {
	if (level <= 1)
	    return 0;

	return experienceArray[level];
    }

    /**
     * Should the fire light or fail?
     */
    public static boolean lightLogs(FiremakingDef def, int firemakingLvl) {
	int levelDiff = firemakingLvl - def.getRequiredLevel();
	if (levelDiff < 0) {
	    return false;
	}
	if (levelDiff >= 20) {
	    return true;
	}
	return DataConversions.random(0, levelDiff + 1) != 0;
    }

    // maxHit
    /**
     * Should the arrow be dropped or disappear
     */
    public static boolean looseArrow(int damage) {
	return DataConversions.random(0, 6) == 0;
    }

    /**
     * Calculate the max hit possible with the given stats
     */
    public static int maxHit(int strength, int weaponPower, boolean burst, boolean superhuman, boolean ultimate, int bonus) {
	double newStrength = (double) ((strength * addPrayers(burst, superhuman, ultimate)) + bonus);

	int fin = (int) ((newStrength * ((((double) weaponPower * 0.00175D) + 0.1D)) + 1.05D) * 0.95D);
	//if (fin > 31)
	// fin = 31;
	return fin;

    }

    /**
     * Gets the min level required to smith a bar
     */
    public static int minSmithingLevel(int barID) {
	int[] levels = { 1, 15, 30, 50, 70, 85 };
	int type = getBarType(barID);
	if (type < 0) {
	    return -1;
	}
	return levels[type];
    }

    public static boolean objectAtFacing(Entity e, int x, int y, int dir) {
	if (dir >= 0 && e instanceof GameObject) {
	    GameObject obj = (GameObject) e;
	    return obj.getType() == 0 && obj.getDirection() == dir && obj.isOn(x, y);
	}
	return false;
    }

    private static int offsetToPercent(int levelDiff) {
	return levelDiff > 40 ? 70 : 30 + levelDiff;
    }

    /**
     * Calulates what one mob should hit on another with meelee
     */
    public static double parseDouble(double number) {
	String numberString = String.valueOf(number);
	return Double.valueOf(numberString.substring(0, numberString.indexOf(".") + 2));
    }

    public static int Rand(int low, int high) {
	return low + r.nextInt(high - low);
    }

    public static int styleBonus(Mob mob, int skill) {
	int style = mob.getCombatStyle();
	if (style == 0) {
	    return 1;
	}
	return (skill == 0 && style == 2) || (skill == 1 && style == 3) || (skill == 2 && style == 1) ? 3 : 0;
    }
    public static int[] rares = new int[]{828, 831, 832, 575, 576, 577, 578, 579, 580, 581, 971, 1316, 1315, 1314, 422, 1289, 1156, 677};
	public static boolean isRareItem(int id) {
		return DataConversions.inArray(rares,id);
	}
    public static int getBarIdFromItem(int itemID) {
    	if(DataConversions.inArray(BRONZE, itemID))
    		return 169;
    	if(DataConversions.inArray(IRON, itemID))
		    return 170;
    	if(DataConversions.inArray(STEEL, itemID))
		    return 171;
    	if(DataConversions.inArray(MITH, itemID))
		    return 173;
    	if(DataConversions.inArray(ADDY, itemID))
		    return 174;
    	if(DataConversions.inArray(RUNE, itemID))
		    return 408;
        return -1;
    	}

    public final static int[] IRON = {6,5,7,8,2,3,9,28, 1075,1, 71, 83, 77, 12, 1258, 89, 0, 670, 1063}; 
	public final static int[] RUNE = {112, 399, 400, 401, 404, 403, 402, 396, 1080, 397, 75, 398, 81, 405, 1262, 93, 98, 674, 1067};
	public final static int[] ADDY = {111, 107, 116, 120, 131, 127, 123, 65, 1079, 69, 74, 86, 80, 204, 1261, 92, 97, 673, 1066};
	public final static int[] MITH = {110, 106, 115, 119, 130, 126, 122, 64, 1078, 68, 73, 85, 79, 203, 1260, 91, 96, 672, 1065};
	public final static int[] STEEL = {109, 105, 114, 118, 129, 125, 121, 63, 1077, 67, 72, 84, 78, 88, 1259, 90, 95, 671, 1064};
	public final static int[] BRONZE = {108, 104, 113, 117, 128, 124, 206, 62, 1076, 66, 70, 82, 76, 87, 156, 87, 205, 669, 1062};

}
