// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.objects;

import engine.Enum;
import engine.Enum.GameObjectType;
import engine.Enum.ItemType;
import engine.gameManager.*;
import engine.math.Vector3fImmutable;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.client.ClientConnection;
import engine.net.client.ClientMessagePump;
import engine.net.client.msg.*;
import engine.powers.poweractions.AbstractPowerAction;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

import static engine.math.FastMath.sqr;
import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup;


public class CharacterItemManager {

    private static final byte inventoryVer = (byte) 0;
    private static final byte bankVer = (byte) 0;
    private static final byte vaultVer = (byte) 0;
    private final AbstractCharacter absCharacter;
    // Mapping of all the items associated with this Manager
    private final ConcurrentHashMap<Integer, Integer> itemIDtoType = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
    // Mapping of all items equipped in this Manager
    // Key = Item Slot
    private final ConcurrentHashMap<Integer, Item> equipped = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
    private final HashSet<Item> inventory = new HashSet<>();
    private final HashSet<Item> bank = new HashSet<>();
    private final HashSet<Item> vault = new HashSet<>();
    private final HashSet<Integer> equipOrder = new HashSet<>();
    public Item goldVault;
    private Account account;
    private Item goldInventory;
    private Item goldBank;
    private boolean bankOpened;
    private boolean vaultOpened;
    private short bankWeight;
    private short inventoryWeight;
    private short equipWeight;
    private short vaultWeight;
    private ClientConnection tradingWith;
    private byte tradeCommitted;
    private boolean tradeSuccess;
    private HashSet<Integer> trading;
    private int goldTradingAmount;
    private int tradeID = 0;
    /*
     * Item Manager Version data
     */
    private byte equipVer = (byte) 0;

    public CharacterItemManager(AbstractCharacter ac) {
        super();
        this.absCharacter = ac;
    }

    public static byte getInventoryVer() {
        return inventoryVer;
    }

    public static byte getBankVer() {
        return bankVer;
    }

    public static byte getVaultVer() {
        return vaultVer;
    }

    public static void takeFromNPC(NPC npc, PlayerCharacter pc, Item take, ClientMessagePump clientMessagePump) {
        ItemBase ib = take.getItemBase();
        if (ib == null)
            return;
        CharacterItemManager itemMan = pc.getCharItemManager();
        if (itemMan == null)
            return;
        CharacterItemManager npcCim = npc.getCharItemManager();
        if (npcCim == null)
            return;
        if (!npcCim.inventoryContains(take)) {
            return;
        }

        if (!itemMan.hasRoomInventory(ib.getWeight()))
            return;
        if (take != null) {
            itemMan.buyFromNPC(take, npc);
            itemMan.updateInventory();
        }
    }

    public void load() {
        loadForGeneric();

        if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
            loadForPlayerCharacter();
        else if (this.absCharacter.getObjectType().equals(GameObjectType.NPC))
            loadForNPC();

    }

    public void loadGoldItems() {

        if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) {
            //other server, just make generic
            this.goldInventory = new MobLoot(this.absCharacter, 0);
            this.goldBank = new MobLoot(this.absCharacter, 0);
            this.goldVault = new MobLoot(this.absCharacter, 0);
            return;
        }

        //create inventory gold if needed
        if (this.goldInventory == null)
            if (this.absCharacter != null && (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) || this.absCharacter.getObjectType().equals(GameObjectType.NPC)))
                this.goldInventory = Item.newGoldItem(this.absCharacter, ItemBase.getItemBase(7), Enum.ItemContainerType.INVENTORY);
            else
                this.goldInventory = new MobLoot(this.absCharacter, 0);

        //create bank gold if needed
        if (this.goldBank == null)
            if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
                this.goldBank = Item.newGoldItem(this.absCharacter, ItemBase.getItemBase(7), Enum.ItemContainerType.BANK);
            else
                this.goldBank = new MobLoot(this.absCharacter, 0);

        //create vault gold if needed
        if (this.goldVault == null)
            if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) {
                this.goldVault = this.account.vaultGold;
            } else
                this.goldVault = new MobLoot(this.absCharacter, 0);

        this.itemIDtoType.put(this.goldInventory.getObjectUUID(), this.goldInventory.getObjectType().ordinal());
        this.itemIDtoType.put(this.goldBank.getObjectUUID(), this.goldBank.getObjectType().ordinal());
        this.itemIDtoType.put(this.goldVault.getObjectUUID(), this.goldVault.getObjectType().ordinal());

    }

    private void loadForPlayerCharacter() {
        ArrayList<Item> al = null;

        // TODO Verify this is an actual account.
        this.account = ((PlayerCharacter) this.absCharacter).getAccount();

        // Get Items for player and vault
        if (ConfigManager.serverType.equals(Enum.ServerType.LOGINSERVER)) //login, only need equipped items
            al = DbManager.ItemQueries.GET_EQUIPPED_ITEMS(this.absCharacter.getObjectUUID());
        else
            al = DbManager.ItemQueries.GET_ITEMS_FOR_PC(this.absCharacter.getObjectUUID());

        for (Item i : al) {

            i.validateItemContainer();
            this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());

            switch (i.containerType) {
                case EQUIPPED:
                    if (this.equipped.containsValue(i) == false) {
                        this.equipped.put((int) i.getEquipSlot(), i);
                        addEquipOrder((int) i.getEquipSlot());
                    }
                    break;
                case BANK:
                    if (i.getItemBase().getType().equals(ItemType.GOLD))
                        this.goldBank = i;
                    else if (this.bank.contains(i) == false)
                        this.bank.add(i);
                    break;
                case INVENTORY:
                    if (i.getItemBase().getType().equals(ItemType.GOLD))
                        this.goldInventory = i;
                    else if (this.inventory.contains(i) == false)
                        this.inventory.add(i);
                    break;
                case VAULT:
                    if (i.getItemBase().getType().equals(ItemType.GOLD))
                        this.goldVault = i;
                    else if (this.vault.contains(i) == false)
                        this.vault.add(i);
                    break;
                default:
                    i.junk();
                    break;
            }

        }

        this.goldVault = this.account.vaultGold;

        //check all gold is created
        //loadGoldItems();
        calculateWeights();
    }

    private void loadForNPC() {
        ArrayList<Item> al = null;

        // Get all items related to this NPC:
        al = DbManager.ItemQueries.GET_ITEMS_FOR_NPC(this.absCharacter.getObjectUUID());

        for (Item i : al) {
            i.validateItemContainer();
            this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());

            switch (i.containerType) {
                case EQUIPPED:
                    if (this.equipped.containsValue(i) == false)
                        this.equipped.put((int) i.getEquipSlot(), i);
                    break;
                case BANK:
                    if (i.getItemBase().getType().equals(ItemType.GOLD))
                        this.goldBank = i;
                    else if (this.bank.contains(i) == false)
                        this.bank.add(i);
                    break;
                case INVENTORY:
                    if (i.getItemBase().getType().equals(ItemType.GOLD))
                        this.goldInventory = i;
                    else if (this.inventory.contains(i) == false)
                        this.inventory.add(i);
                    break;
                default:
                    i.junk();
                    break;
            }
        }

        //check all gold is created
        //loadGoldItems();
    }

    private void loadForGeneric() {
        this.bankWeight = 0;
        this.inventoryWeight = 0;
        this.equipWeight = 0;
        this.vaultWeight = 0;

        //check all gold is created
        //loadGoldItems();
        // Always initialize with bank and vault closed
        bankOpened = false;
        vaultOpened = false;
    }

    //Positve Amount = TO BUILDING; Negative Amount = FROM BUILDING. flip signs for Player inventory.
    public synchronized boolean transferGoldToFromBuilding(int amount, AbstractWorldObject object) {
        if (this.absCharacter.getObjectType() != GameObjectType.PlayerCharacter)
            return false;

        PlayerCharacter player = (PlayerCharacter) this.absCharacter;

        switch (object.getObjectType()) {
            case Building:
                Building building = (Building) object;

                if (!this.getGoldInventory().validForInventory(player.getClientConnection(), player, this))
                    return false;

                if (amount < 0 && amount > building.getStrongboxValue())
                    return false;

                // Not enough gold in inventory to transfer to tree

                if ((amount > 0) &&
                        (this.getGoldInventory().getNumOfItems() - amount < 0)) {
                    sendErrorPopup(player, 28);
                    return false;
                }

                if (this.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT) {
                    ErrorPopupMsg.sendErrorPopup(player, 202);
                    return false;
                }


                // Not enough gold to transfer to inventory from tree

                if ((amount < 0) &&
                        (building.getStrongboxValue() + amount < 0)) {
                    sendErrorPopup(player, 127);
                    return false;
                }

                if (amount < 0)
                    if (!building.hasFunds(-amount))
                        return false;

                //Verify player can access building to transfer goldItem

                if (!BuildingManager.playerCanManage(player, building))
                    return false;

                if (building.getStrongboxValue() + amount > building.getMaxGold()) {
                    ErrorPopupMsg.sendErrorPopup(player, 201);
                    return false;
                }

                if (this.getOwner().getCharItemManager().getGoldTrading() > 0) {
                    if (this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter))
                        ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.getOwner(), 195);
                    return false;
                }

                if (!this.modifyInventoryGold(-amount)) {

                    Logger.error(player.getName() + " transfer amount = " + amount + " ; Gold Inventory = " + this.getGoldInventory().getNumOfItems());

                    //  ChatManager.chatSystemError(player, "You do not have this Gold.");
                    return false;
                }

                if (!building.transferGold(amount, false)) {

                    Logger.error(player.getName() + " transfer amount = " + amount + " ; Gold Inventory = " + this.getGoldInventory().getNumOfItems() + "; Building Strongbox = " + building.getStrongboxValue());

                    //ChatManager.chatSystemError(player, "Something went terribly wrong. Contact CCR.");
                    return false;
                }

                break;
            case Warehouse:

                Warehouse warehouse = (Warehouse) object;

                if (amount < 0) {
                    if (!warehouse.deposit((PlayerCharacter) this.absCharacter, this.getGoldInventory(), amount * -1, true, true)) {

                        ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.absCharacter, 203);
                        return false;
                    }
                } else {
                    if (!warehouse.withdraw((PlayerCharacter) this.absCharacter, this.getGoldInventory().getItemBase(), amount * -1, true, true)) {

                        ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.absCharacter, 203);
                        return false;
                    }

                }

                break;

        }
        return true;
    }

    /*
     * Item Controls
     */
    public synchronized boolean modifyInventoryGold(int modifyValue) {

        Item goldItem;
        PlayerCharacter player;
        boolean success = false;

        goldItem = getGoldInventory();

        if (goldItem == null) {
            Logger.error("ModifyInventoryGold", "Could not create gold item");
            return success;
        }

        if (this.getGoldInventory().getNumOfItems() + modifyValue > MBServerStatics.PLAYER_GOLD_LIMIT) {
            return false;
        }

        if (this.getGoldInventory().getNumOfItems() + modifyValue < 0)
            return false;

        // No database update for npc's gold values so we use the player object
        // for flow control later on.

        if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter)
            player = (PlayerCharacter) this.absCharacter;
        else
            player = null;

        // If this is an update for a player character update the database

        if (player != null)
            try {
                if (!DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + modifyValue)) {
                    return false;
                }


                success = true;
            } catch (Exception e) {
                Logger.error("ModifyInventoryGold", "Error writing to database");
            }

        // Update in-game gold values for character
        goldItem.setNumOfItems(goldItem.getNumOfItems() + modifyValue);
        UpdateGoldMsg ugm = new UpdateGoldMsg(this.absCharacter);
        ugm.configure();

        Dispatch dispatch = Dispatch.borrow(player, ugm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        return success;
    }

    public synchronized boolean tradeRequest(TradeRequestMsg msg) {

        PlayerCharacter source = (PlayerCharacter) this.getOwner();
        PlayerCharacter target = PlayerCharacter.getFromCache(msg.getPlayerID());
        Dispatch dispatch;

        if (!canTrade(source, target)) {
            ChatManager.chatSystemError(source, "Can't currently trade with target player");
            return false;
        }

        // TODO uncomment this block after we determine when we
        // setBankOpen(false) and setVaultOpen(false)
        CharacterItemManager cim1 = source.getCharItemManager();
        CharacterItemManager cim2 = target.getCharItemManager();

        if (cim1 == null)
            return false;

        if (cim2 == null)
            return false;

        dispatch = Dispatch.borrow(target, msg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
        return true;

    }

    public synchronized boolean invalidTradeRequest(InvalidTradeRequestMsg msg) {
        PlayerCharacter requester = PlayerCharacter.getFromCache(msg.getRequesterID());
        Dispatch dispatch;

        dispatch = Dispatch.borrow(requester, msg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
        return true;

    }

    public synchronized boolean canTrade(PlayerCharacter playerA, PlayerCharacter playerB) {

        if (playerA == null || playerB == null)
            return false;

        //make sure both are alive
        if (!playerA.isAlive() || !playerB.isAlive())
            return false;

        //distance check
        Vector3fImmutable aLoc = playerA.getLoc();
        Vector3fImmutable bLoc = playerB.getLoc();

        if (aLoc.distanceSquared2D(bLoc) > sqr(MBServerStatics.TRADE_RANGE))
            return false;

        //visibility check
        if (!playerA.canSee(playerB) || !playerB.canSee(playerA))
            return false;

        if (playerA.lastBuildingAccessed != 0) {
            ManageCityAssetsMsg mca = new ManageCityAssetsMsg();
            mca.actionType = 4;
            mca.setTargetType(Enum.GameObjectType.Building.ordinal());
            mca.setTargetID(playerA.lastBuildingAccessed);
            Dispatch dispatch = Dispatch.borrow(playerA, mca);
            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
            playerA.lastBuildingAccessed = 0;
        }

        return true;
    }

    public synchronized boolean acceptTradeRequest(AcceptTradeRequestMsg msg) {

        PlayerCharacter source = (PlayerCharacter) this.getOwner();
        PlayerCharacter target = PlayerCharacter.getFromCache(msg.getTargetID());

        Dispatch dispatch;

        if (source == null || !source.isAlive())
            return false;

        if (target == null || !target.isAlive())
            return false;

        if (this.tradingWith != null)
            return false;

        if (!canTrade(source, target))
            return false;

        // verify characterTarget is in range
        if (source.getLoc().distanceSquared2D(target.getLoc()) > sqr(MBServerStatics.TRADE_RANGE))
            return false;

        // TODO uncomment this block after we determine when we
        // setBankOpen(false) and setVaultOpen(false)
        /*
         * CharacterItemManager cim1 = source.getCharItemManager();
         * CharacterItemManager cim2 = characterTarget.getCharItemManager(); if (cim1 ==
         * null) return false; if (cim2 == null) return false; if (cim1.isBankOpen())
         * return false; if (cim2.isVaultOpen()) return false;
         */
        ClientConnection sourceConn = source.getClientConnection();
        ClientConnection targetConn = target.getClientConnection();

        if (sourceConn == null)
            return false;

        if (targetConn == null)
            return false;


        CharacterItemManager toTradeWith = target.getCharItemManager();

        if (toTradeWith == null)
            return false;

        Account sourceAccount = source.getAccount();
        Account targetAccount = target.getAccount();

        UpdateVaultMsg uvmSource = new UpdateVaultMsg(sourceAccount);
        UpdateVaultMsg uvmTarget = new UpdateVaultMsg(targetAccount);

        dispatch = Dispatch.borrow(source, uvmSource);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);

        dispatch = Dispatch.borrow(target, uvmTarget);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);

        this.setVaultOpen(false);
        toTradeWith.setVaultOpen(false);
        this.setBankOpen(false);
        toTradeWith.setBankOpen(false);

        OpenTradeWindowMsg otwm = new OpenTradeWindowMsg(msg.getUnknown01(), source, target);

        // Only start trade if both players aren't already trading with
        // someone

        if (this.getTradingWith() != null || toTradeWith.getTradingWith() != null)
            return false;

        this.initializeTrade();
        toTradeWith.initializeTrade();
        this.setTradingWith(targetConn);
        toTradeWith.setTradingWith(sourceConn);
        this.tradeID = msg.getUnknown01();
        toTradeWith.tradeID = msg.getUnknown01();

        dispatch = Dispatch.borrow(source, otwm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);

        dispatch = Dispatch.borrow(target, otwm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);

        return true;
    }

    public synchronized boolean addItemToTradeWindow(AddItemToTradeWindowMsg msg) {
        PlayerCharacter source = (PlayerCharacter) this.getOwner();
        Dispatch dispatch;

        if (source == null || !source.isAlive())
            return false;


        ClientConnection ccOther = this.getTradingWith();

        if (ccOther == null)
            return false;

        PlayerCharacter other = ccOther.getPlayerCharacter();

        if (other == null || !other.isAlive())
            return false;

        CharacterItemManager tradingWith = other.getCharItemManager();

        if (tradingWith == null)
            return false;

        if (!canTrade(source, other))
            return false;

        Item i = Item.getFromCache(msg.getItemID());

        if (i == null)
            return false;

        if (!this.doesCharOwnThisItem(i.getObjectUUID()))
            return false;

        //can't add item to trade window twice
        if (this.tradingContains(i))
            return false;

        //dupe check
        if (!i.validForInventory(source.getClientConnection(), source, this))
            return false;

        if (!tradingWith.hasRoomTrade(i.getItemBase().getWeight())) {
            dispatch = Dispatch.borrow(source, msg);
            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
            return false;
        }

        UpdateTradeWindowMsg utwm = new UpdateTradeWindowMsg(source, other);

        this.setTradeCommitted((byte) 0);
        tradingWith.setTradeCommitted((byte) 0);

        this.addItemToTrade(i);

        dispatch = Dispatch.borrow(other, msg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);

        modifyCommitToTrade();

        dispatch = Dispatch.borrow(other, utwm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
        stripTempEnchants(i);
        return true;
    }

    public synchronized boolean addGoldToTradeWindow(AddGoldToTradeWindowMsg msg) {

        PlayerCharacter source = (PlayerCharacter) this.getOwner();
        Dispatch dispatch;

        if (source == null || !source.isAlive())
            return false;


        ClientConnection ccOther = this.getTradingWith();

        if (ccOther == null)
            return false;

        PlayerCharacter other = ccOther.getPlayerCharacter();

        if (other == null || !other.isAlive())
            return false;

        CharacterItemManager tradingWith = other.getCharItemManager();

        if (tradingWith == null)
            return false;

        UpdateTradeWindowMsg utwm = new UpdateTradeWindowMsg(other, source);
        UpdateTradeWindowMsg utwmOther = new UpdateTradeWindowMsg(source, other);

        if (!canTrade(source, other))
            return false;

        this.setTradeCommitted((byte) 0);
        tradingWith.setTradeCommitted((byte) 0);

        int amt = msg.getAmount();

        if (amt <= 0) {
            Logger.info(source.getFirstName() + " added negative gold to trade window. Dupe attempt FAILED!");
            return false;
        }

        if (amt > MBServerStatics.PLAYER_GOLD_LIMIT)
            return false;

        if (this.getGoldInventory().getNumOfItems() - amt < 0)
            return false;

        this.addGoldToTrade(amt);

        // BONUS CODE BELOW:  Thanks some unknown retard!
        //		sourceItemMan.updateInventory(sourceItemMan.getInventory(), true);

        UpdateGoldMsg ugm = new UpdateGoldMsg(source);
        ugm.configure();

        modifyCommitToTrade();

        dispatch = Dispatch.borrow(source, utwm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        dispatch = Dispatch.borrow(source, ugm);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        dispatch = Dispatch.borrow(other, utwmOther);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
        return true;

    }

    public synchronized boolean uncommitToTrade(UncommitToTradeMsg msg) {

        PlayerCharacter source = (PlayerCharacter) this.getOwner();

        if (source == null || !source.isAlive())
            return false;

        CharacterItemManager sourceItemMan = source.getCharItemManager();

        if (sourceItemMan == null)
            return false;

        sourceItemMan.setTradeCommitted((byte) 0);

        ClientConnection ccOther = sourceItemMan.getTradingWith();

        if (ccOther == null)
            return false;

        PlayerCharacter other = ccOther.getPlayerCharacter();

        if (other == null)
            return false;

        if (!canTrade(source, other))
            return false;

        return modifyCommitToTrade();
    }

    public synchronized boolean commitToTrade(CommitToTradeMsg msg) {

        PlayerCharacter source = (PlayerCharacter) this.getOwner();

        if (source == null || !source.isAlive())
            return false;


        this.setTradeCommitted((byte) 1);

        ClientConnection ccOther = this.getTradingWith();

        if (ccOther == null)
            return false;

        PlayerCharacter other = ccOther.getPlayerCharacter();

        if (other == null || !other.isAlive())
            return false;

        CharacterItemManager tradingWith = other.getCharItemManager();

        if (tradingWith == null)
            return false;

        if (!canTrade(source, other))
            return false;

        modifyCommitToTrade();

        if (this.getTradeCommitted() == (byte) 1 && tradingWith.getTradeCommitted() == (byte) 1) {
            int tradeID = this.tradeID;
            CloseTradeWindowMsg ctwm1 = new CloseTradeWindowMsg(source, tradeID);
            CloseTradeWindowMsg ctwm2 = new CloseTradeWindowMsg(other, tradeID);
            this.commitTrade();
            this.closeTradeWindow(ctwm1, false);
            other.getCharItemManager().closeTradeWindow(ctwm2, false);
        }
        return true;
    }

    private synchronized boolean modifyCommitToTrade() {
        CharacterItemManager man1 = this;

        if (this.getTradingWith() == null)
            return false;

        if (this.getTradingWith().getPlayerCharacter() == null)
            return false;
        CharacterItemManager man2 = this.getTradingWith().getPlayerCharacter().getCharItemManager();
        Dispatch dispatch;

        if (man1 == null || man2 == null)
            return false;

        ModifyCommitToTradeMsg modify = new ModifyCommitToTradeMsg(this.getOwner(), man2.getOwner(), man1.getTradeCommitted(),
                man2.getTradeCommitted());

        dispatch = Dispatch.borrow((PlayerCharacter) this.getOwner(), modify);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        dispatch = Dispatch.borrow((PlayerCharacter) man2.getOwner(), modify);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        return true;

    }

    public synchronized boolean closeTradeWindow(CloseTradeWindowMsg msg, boolean sourceTrade) {


        Dispatch dispatch;

        PlayerCharacter source = (PlayerCharacter) this.getOwner();
        if (source == null)
            return false;

        CharacterItemManager sourceItemMan = source.getCharItemManager();

        if (sourceItemMan == null)
            return false;

        int tradeID = this.tradeID;
        CloseTradeWindowMsg closeMsg = new CloseTradeWindowMsg(source, tradeID);

        dispatch = Dispatch.borrow(source, closeMsg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

        if (!sourceTrade) {
            sourceItemMan.endTrade();
            return true;
        }

        ClientConnection cc2 = sourceItemMan.getTradingWith();

        if (cc2 == null || cc2.getPlayerCharacter() == null) {
            sourceItemMan.endTrade();
            return true;
        }

        sourceItemMan.endTrade();


        cc2.getPlayerCharacter().getCharItemManager().closeTradeWindow(msg, false);


        return true;
    }

    public Item getGoldInventory() {
        if (this.goldInventory == null)
            loadGoldItems();
        return this.goldInventory;
    }

    public Item getGoldBank() {
        if (this.goldBank == null)
            loadGoldItems();
        return this.goldBank;
    }

    public Item getGoldVault() {
        if (this.goldVault == null)
            loadGoldItems();
        return this.goldVault;
    }

    public void addEquipOrder(int slot) {
        synchronized (this.equipOrder) {
            Integer iSlot = slot;
            if (this.equipOrder.contains(iSlot))
                this.equipOrder.remove(iSlot);
            this.equipOrder.add(slot);
        }
    }

    public synchronized boolean doesCharOwnThisItem(int itemID) {
        return this.itemIDtoType.containsKey(itemID);
    }

    public synchronized boolean junk(Item i) {
        return junk(i, true);
    }

    public synchronized boolean recycle(Item i) {
        if (i.getObjectType() == GameObjectType.Item)
            return junk(i, false);
        else {
            if (this.removeItemFromInventory(i) == false)
                return false;
            ((MobLoot) i).recycle((NPC) this.absCharacter);
            calculateInventoryWeight();
            return true;
        }
    }

    // The DeleteItemMsg takes care of updating inventory, so we don't want to do it separately
    public synchronized boolean delete(Item i) {
        return junk(i, false);
    }

    //cleanup an item from CharacterItemManager if it doesn't belong here
    public synchronized boolean cleanupDupe(Item i) {
        if (i == null)
            return false;

        if (i.getItemBase().getType().equals(ItemType.GOLD)) {
            if (this.getGoldInventory() != null) {
                if (i.getObjectUUID() == this.getGoldInventory().getObjectUUID())
                    this.goldInventory = null;
            } else if (this.getGoldBank() != null) {
                if (i.getObjectUUID() == this.getGoldBank().getObjectUUID())
                    this.goldBank = null;
            }
            return true;
        }

        byte slot = i.getEquipSlot();

        if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
            return false;

        // remove it from other lists:
        this.remItemFromLists(i, slot);
        this.itemIDtoType.remove(i.getObjectUUID());

        calculateWeights();
        return true;
    }

    public synchronized boolean consume(Item i) {
        i.decrementChargesRemaining();
        if (i.getChargesRemaining() > 0)
            return true;
        return junk(i, true);
    }

    private synchronized boolean junk(Item i, boolean updateInventory) {
        if (i.getItemBase().getType().equals(ItemType.GOLD)) {
            if (this.getGoldInventory().getObjectUUID() == i.getObjectUUID())
                if (DbManager.ItemQueries.UPDATE_GOLD(i, 0)) {
                    this.getGoldInventory().setNumOfItems(0);
                    if (updateInventory)
                        updateInventory();
                    return true;
                } else {
                    return false;
                }
            if (!(this.absCharacter.getObjectType().equals(GameObjectType.Mob)))
                return false;
        }

        byte slot = i.getEquipSlot();

        if (this.doesCharOwnThisItem(i.getObjectUUID()) == false && this.absCharacter.getObjectType() != GameObjectType.Mob && (i.containerType != Enum.ItemContainerType.FORGE))
            return false;

        // remove it from other lists:
        this.remItemFromLists(i, slot);
        this.itemIDtoType.remove(i.getObjectUUID());

        i.junk();

        //Why are we adding junked items?!

        //		if (i.getObjectType() != GameObjectType.MobLoot)
        //			CharacterItemManager.junkedItems.add(i);


        calculateWeights();

        if (updateInventory)
            // Send the new inventory
            //updateInventory(i, false); this line was causing entire inventory to disappear
            updateInventory(this.getInventory(), true);

        return true;
    }

    public synchronized boolean moveItemToInventory(Item i) {

        boolean fromEquip = false;
        synchronized (this) {
            byte slot = i.getEquipSlot();

            //Skip if NOT in vault.
            if (i.containerType != Enum.ItemContainerType.VAULT)
                if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
                    return false;

            // Only valid from bank, equip and vault
            if (!bankContains(i) && !equippedContains(i) && !vaultContains(i))
                return false;

            if (equippedContains(i)) {
                fromEquip = true;
                ItemBase ib = i.getItemBase();
                if (ib != null && ib.getType().equals(ItemType.GOLD))
                    this.absCharacter.cancelOnUnEquip();
            }

            // check to see what type of AbstractCharacter subclass we have stored
            if (this.absCharacter.getClass() == PlayerCharacter.class) {
                if (!i.moveItemToInventory((PlayerCharacter) this.absCharacter))
                    return false;
            } else if (!i.moveItemToInventory((NPC) this.absCharacter))
                return false;

            // remove it from other lists:
            this.remItemFromLists(i, slot);

            // add to Inventory
            this.inventory.add(i);
            i.addToCache();
            this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());

            calculateWeights();
        }

        //Apply bonuses if from equip
        if (fromEquip && this.absCharacter != null) {
            this.absCharacter.applyBonuses();
            if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
                this.absCharacter.incVer();
        }

        return true;
    }

    public synchronized boolean moveItemToBank(Item i) {
        byte slot = i.getEquipSlot();

        if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
            return false;

        // Item must be in inventory to move to bank
        if (!this.inventory.contains(i))
            return false;

        // check to see what type of AbstractCharacter subclass we have stored
        if (this.absCharacter.getClass() == PlayerCharacter.class) {
            if (!i.moveItemToBank((PlayerCharacter) this.absCharacter))
                return false;
        } else if (!i.moveItemToBank((NPC) this.absCharacter))
            return false;

        // remove it from other lists:
        this.remItemFromLists(i, slot);

        // add to Bank
        this.bank.add(i);
        i.addToCache();

        calculateWeights();
        stripTempEnchants(i);
        return true;
    }

    public synchronized boolean moveGoldToBank(Item from, int amt) {
        if (from == null)
            return false;
        if (from.getNumOfItems() - amt < 0)
            return false;
        if (this.goldBank.getNumOfItems() + amt > MBServerStatics.BANK_GOLD_LIMIT) {
            if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) {
                PlayerCharacter pc = (PlayerCharacter) this.absCharacter;
                if (pc.getClientConnection() != null)
                    ErrorPopupMsg.sendErrorPopup(pc, 202);
                return false;
            }
        }

        if (!DbManager.ItemQueries.MOVE_GOLD(from, this.getGoldBank(), amt))
            return false;
        from.setNumOfItems(from.getNumOfItems() - amt);
        this.goldBank.setNumOfItems(this.goldBank.getNumOfItems() + amt);
        return true;
    }

    public synchronized boolean moveGoldToVault(Item from, int amt) {
        if (from == null)
            return false;
        if (from.getNumOfItems() - amt < 0)
            return false;
        if (!DbManager.ItemQueries.MOVE_GOLD(from, this.account.vaultGold, amt))
            return false;
        from.setNumOfItems(from.getNumOfItems() - amt);
        this.account.vaultGold.setNumOfItems(this.goldVault.getNumOfItems() + amt);
        return true;
    }

    public synchronized boolean moveGoldToInventory(Item from, int amt) {
        if (from == null)
            return false;
        if (from.getNumOfItems() - amt < 0 || amt < 1)
            return false;

        if (this.goldInventory.getNumOfItems() + amt > MBServerStatics.PLAYER_GOLD_LIMIT) {
            if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) {
                PlayerCharacter pc = (PlayerCharacter) this.absCharacter;
                if (pc.getClientConnection() != null)
                    ErrorPopupMsg.sendErrorPopup(pc, 202);
                return false;
            }
        }

        if (from instanceof MobLoot) {
            if (!DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(),
                    this.goldInventory.getNumOfItems() + amt))
                return false;
        } else if (!DbManager.ItemQueries.MOVE_GOLD(from, this.goldInventory, amt))
            return false;
        from.setNumOfItems(from.getNumOfItems() - amt);
        this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + amt);
        return true;
    }

    //This is called by the addGold devCmd.
    public synchronized boolean addGoldToInventory(int amt, boolean fromDevCmd) {

        if (this.absCharacter == null || (!(this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))))
            return false;

        if (this.getGoldInventory().getNumOfItems() + amt > MBServerStatics.PLAYER_GOLD_LIMIT) {
            return false;
        }

        if (this.getGoldInventory().getNumOfItems() + amt < 0)
            return false;


        boolean worked = DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + amt);
        if (worked) {
            //log this since it's technically a dupe. Only use on test server!
            if (fromDevCmd) {
                String logString = this.absCharacter.getName() + " added " + amt + " gold to their inventory";
                Logger.info(logString);
            }
            this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + amt);
        }
        return worked;
    }

    //Used to trainsfer gold from one inventory to another, for steal, etc.
    public boolean transferGoldToMyInventory(AbstractCharacter tar, int amount) {
        if (tar == null)
            return false;

        CharacterItemManager tarCim = tar.getCharItemManager();
        if (tarCim == null)
            return false;

        if (this.getGoldInventory().getNumOfItems() + amount < 0)
            return false;

        if (this.getGoldInventory().getNumOfItems() + amount > MBServerStatics.PLAYER_GOLD_LIMIT)
            return false;

        if (tarCim.getGoldInventory().getNumOfItems() - amount < 0)
            return false;

        if (tarCim.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT)
            return false;

        synchronized (this) {
            synchronized (tarCim) {
                if (!tarCim.addGoldToInventory(0 - amount, false)) //remove gold from target
                    return false;
                if (!addGoldToInventory(amount, false)) //add to this inventory
                    return false;
            }
        }
        return true;
    }

    public synchronized boolean moveItemToVault(Item i) {
        byte slot = i.getEquipSlot();

        //		if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
        //			return false;

        // Item must be in inventory to move to vault
        if (!this.inventory.contains(i))
            return false;

        // check to see what type of AbstractCharacter subclass we have stored
        if (this.absCharacter.getClass() == PlayerCharacter.class) {
            if (!i.moveItemToVault(this.account))
                return false;
        } else
            return false; // NPC's dont have vaults!

        // remove it from other lists:
        this.remItemFromLists(i, slot);

        // add to Vault
        i.addToCache();

        calculateWeights();
        stripTempEnchants(i);
        return true;
    }

    private static void stripTempEnchants(Item i) {
        i.clearEffects();
    }

    //Used for buying MobEquipment from NPC
    //Handles the gold transfer aspect

    // This removes ingame item from inventory for loot.
    private synchronized boolean removeItemFromInventory(Item i) {
        if (i.getItemBase().getType().equals(ItemType.GOLD)) {
            if (i.getObjectUUID() != this.getGoldInventory().getObjectUUID())
                return false;
            if (!DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) {
                return false;
            }

        } else {
            if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
                return false;
            if (this.inventory.contains(i)) {
                this.inventory.remove(i);
                this.itemIDtoType.remove(i.getObjectUUID());
                return true;
            }
        }
        // tell client we're removing item
        updateInventory(i, false);
        return false;
    }

    // This adds item to inventory for loot. Validity checks already handled
    public synchronized boolean addItemToInventory(Item i) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            if (this.absCharacter.getObjectType() == GameObjectType.Mob) {
                if (this.goldInventory == null)
                    loadGoldItems();
                this.goldInventory.setNumOfItems(this.goldInventory.getNumOfItems() + i.getNumOfItems());
            } else {
                int amt = i.getNumOfItems();
                if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, this.goldInventory.getNumOfItems() + amt)) {
                    updateInventory();
                    return true;
                }

                return false;
            }
        if (i.getItemBase().getType().equals(ItemType.RESOURCE) && this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter)) {
            boolean added = false;
            for(Item item : this.getInventory()){
                if(item.getItemBaseID() == i.getItemBaseID()){
                    if(item.getNumOfItems() + i.getNumOfItems() <= Warehouse.getMaxResources().get(item.getItemBaseID())) {
                        item.setNumOfItems(item.getNumOfItems() + i.getNumOfItems());
                        updateInventory();
                        added = true;
                    }
                }
            }
            if(!added){
                this.inventory.add(i);
                this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());

                ItemBase ib = i.getItemBase();
                if (ib != null)
                    this.inventoryWeight += ib.getWeight();
            }
        }else {
            this.inventory.add(i);
            this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());

            ItemBase ib = i.getItemBase();
            if (ib != null)
                this.inventoryWeight += ib.getWeight();
        }
        return true;
    }

    //called for adding gold of a specified amount
    public synchronized boolean addItemToInventory(Item i, int amount) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            return DbManager.ItemQueries.UPDATE_GOLD(this.getGoldInventory(), this.goldInventory.getNumOfItems() + amount);
        return false;
    }

    public boolean equipItem(Item i, byte slot) {

        synchronized (this) {
            byte curSlot = i.getEquipSlot(); // Should be 0

            if (this.doesCharOwnThisItem(i.getObjectUUID()) == false && this.absCharacter.getObjectType() != GameObjectType.Mob) {
                Logger.error("Doesnt own item");
                return false;
            }

            // Item must be in inventory to equip
            if (!this.inventory.contains(i) && this.absCharacter.getObjectType() != GameObjectType.Mob)
                return false;

            // make sure player can equip item
            if (i.getItemBase() == null)
                return false;
            if (!i.getItemBase().canEquip(slot, this, absCharacter, i) && this.absCharacter.getObjectType() != GameObjectType.Mob)
                return false;

            // check to see if item is already there.
            Item old = this.equipped.get((int) slot);
            if (old != null) {
                Logger.error("already equipped");
                return false;
            }

            // check to see what type of AbstractCharacter subclass we have stored
            if (this.absCharacter.getClass() == PlayerCharacter.class) {
                if (!i.equipItem((PlayerCharacter) this.absCharacter, slot))
                    return false;
            } else if (this.absCharacter.getObjectType() == GameObjectType.Mob) {
                if (!i.equipItem((Mob) this.absCharacter, slot)) {
                    Logger.error("Failed to set Equip");
                    return false;
                }

            } else if (!i.equipItem((NPC) this.absCharacter, slot))
                return false;

            // remove it from other lists:
            this.remItemFromLists(i, slot);

            // add to Equipped
            this.equipped.put((int) slot, i);
            i.addToCache();

            addEquipOrder(i.getEquipSlot());

            //calculateWeights();
        }

        //Apply Bonuses and update player
        if (this.absCharacter != null) {
            this.absCharacter.applyBonuses();
            if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
                this.absCharacter.incVer();
        }

        return true;
    }

    public synchronized boolean buyFromNPC(Building vendorBuilding, int cost, int buildingDeposit) {

        Item gold = this.getGoldInventory();

        if (cost <= 0 || (gold.getNumOfItems() - cost) < 0){
            ChatManager.chatSystemError((PlayerCharacter)this.getOwner(),"Not Enough Gold: " + "COST: " + cost);
            return false;
        }


        if (this.getOwner() != null && this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter)) {
            if (this.goldTradingAmount > 0) {
                ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.getOwner(), 195);
                return false;
            }
        }

        // Create gold from screatch instead of building strongbox
        // if the NPC is not slotted.

        if (vendorBuilding == null) {
            return this.modifyInventoryGold(-cost);
        }


        if (vendorBuilding.getStrongboxValue() + cost > vendorBuilding.getMaxGold()) {

            if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) {
                PlayerCharacter pc = (PlayerCharacter) this.absCharacter;
                if (pc.getClientConnection() != null)
                    ErrorPopupMsg.sendErrorPopup(pc, 206);
            }
            return false;
        }


        // Update strongbox and inventory gold
        if (!this.modifyInventoryGold(-cost)) {
            return false;
        }
        City buildingCity = vendorBuilding.getCity();

        if (buildingCity != null) {
            buildingCity.transactionLock.writeLock().lock();
            try {
                if (!vendorBuilding.transferGold(buildingDeposit, true)) {
                    return false;
                }
            } catch (Exception e) {
                Logger.error(e);
                return false;
            } finally {
                buildingCity.transactionLock.writeLock().unlock();
            }
        } else if (!vendorBuilding.transferGold(buildingDeposit, true)) {
            return false;
        }
        return true;
    }

    //Used for selling items to NPC
    public synchronized boolean sellToNPC(Building building, int cost, Item item) {

        // Create gold from screatch instead of building strongbox
        // if the NPC is not slotted.

        if (this.getGoldInventory().getNumOfItems() + cost < 0)
            return false;

        if (this.getGoldInventory().getNumOfItems() + cost > MBServerStatics.PLAYER_GOLD_LIMIT)
            return false;


        if (this.getOwner().getCharItemManager().getGoldTrading() > 0) {
            if (this.getOwner().getObjectType().equals(GameObjectType.PlayerCharacter))
                ErrorPopupMsg.sendErrorPopup((PlayerCharacter) this.getOwner(), 195);
            return false;
        }


        if (building == null) {
            return this.modifyInventoryGold(cost);
        }

        //make sure strongbox can afford gold.

        if (!building.hasFunds(cost))
            return false;

        if ((building.getStrongboxValue() - cost) < 0)
            return false;

        // Update strongbox and inventory gold

        if (!building.transferGold(-cost, false))
            return false;

        return this.modifyInventoryGold(cost);
    }

    /**
     * This sells an item to an npc
     *
     * @return True on success
     */
    public synchronized boolean sellToNPC(Item itemToSell, NPC npc) {

        CharacterItemManager itemMan;

        if (itemToSell == null || npc == null)
            return false;

        itemMan = npc.getCharItemManager();

        if (itemMan == null)
            return false;

        //test npc inventory is not full

        synchronized (this) {
            synchronized (itemMan) {
                if (!this.doesCharOwnThisItem(itemToSell.getObjectUUID()))
                    return false;
                // attempt to transfer item in db

                boolean sdrMerchant = false;

                if (npc.getContractID() >= 1900 && npc.getContractID() <= 1906)
                    sdrMerchant = true;

                if (sdrMerchant) {
                    this.delete(itemToSell);
                    this.updateInventory();

                } else if (!itemToSell.moveItemToInventory(npc))
                    return false;

                // db transfer successfull, remove from this character
                // skip this check if this is a mobLoot item (which is not in any inventory)
                if (!sdrMerchant)
                    if (!removeItemFromInventory(itemToSell))
                        return false;

                // add item to looter.
                if (!sdrMerchant)
                    if (!itemMan.addItemToInventory(itemToSell))
                        return false;
            }
        }

        // calculate new weights
        calculateInventoryWeight();
        itemMan.calculateInventoryWeight();
        return true;
    }

    /**
     * This buys an item from an npc
     * Handles transfer of item.
     *
     * @return True on success
     */
    public synchronized boolean buyFromNPC(Item purchasedItem, NPC npc) {

        CharacterItemManager itemMan;
        ItemBase itemBase;

        if (purchasedItem == null || npc == null)
            return false;

        itemMan = npc.getCharItemManager();

        if (itemMan == null)
            return false;


        synchronized (this) {
            synchronized (itemMan) {
                itemBase = purchasedItem.getItemBase();

                if (itemBase == null)
                    return false;

                //test inventory is not full

                if (!hasRoomInventory(itemBase.getWeight()))
                    return false;

                if (!itemMan.inventory.contains(purchasedItem))
                    return false;
                // attempt to transfer item in db

                if (purchasedItem.getObjectType() == GameObjectType.MobLoot) {

                    Item newItem = ((MobLoot) purchasedItem).promoteToItem((PlayerCharacter) this.absCharacter);
                    if (newItem == null)
                        return false;

                    if (!itemMan.removeItemFromInventory(purchasedItem))
                        return false;

                    if (!addItemToInventory(newItem))
                        return false;
                    //Item was created and still a mobloot item, remove from npc production list in db.

                    DbManager.NPCQueries.REMOVE_FROM_PRODUCTION_LIST(purchasedItem.getObjectUUID(), npc.getObjectUUID());


                } else {
                    if (!purchasedItem.moveItemToInventory((PlayerCharacter) this.absCharacter))
                        return false;

                    if (purchasedItem.getValue() != purchasedItem.getMagicValue()) {
                        DbManager.ItemQueries.UPDATE_VALUE(purchasedItem, 0);
                        purchasedItem.setValue(0);
                    }

                    // db transfer successfull, remove from this character
                    // skip this check if this is a mobLoot item (which is not in any inventory)
                    if (!itemMan.removeItemFromInventory(purchasedItem))
                        return false;

                    // add item to looter.

                    if (!addItemToInventory(purchasedItem))
                        return false;
                }

            }
        }

        // calculate new weights
        calculateInventoryWeight();
        itemMan.calculateInventoryWeight();
        return true;
    }

    /**
     * Loot an item from an AbstractCharacter. Call this function on
     * the CharacterItemManager of the current item owner, not the looter.
     * This method will verify that the looter can receive the item
     * (e.g. inventory isn't full).
     *
     * @param i      Item being looted
     * @param looter Player looting the item
     * @param origin ClientConnection
     * @return True on success
     */
    public synchronized Item lootItemFromMe(Item i, PlayerCharacter looter, ClientConnection origin) {
        return lootItemFromMe(i, looter, origin, false, -1);
    }

    //This function is used for both looting and stealing
    public synchronized Item lootItemFromMe(Item lootItem, PlayerCharacter lootingPlayer, ClientConnection origin, boolean fromSteal, int amount) {

        //TODO this function should have more logging
        // make sure lootingPlayer exists
        if (lootingPlayer == null)
            return null;

        // get looters item manager
        CharacterItemManager looterItems = lootingPlayer.getCharItemManager();

        if (looterItems == null)
            return null;

        if (fromSteal) {
            if (!this.absCharacter.isAlive())
                return null;
        } else if (!this.absCharacter.canBeLooted())
            return null;

        MobLoot mobLoot = null;
        if (lootItem instanceof MobLoot) {
            mobLoot = (MobLoot) lootItem;
            if (mobLoot.isDeleted())
                return null;
        }

        //Lock both ItemManagers; lower ID first
        CharacterItemManager lockFirst;
        CharacterItemManager lockSecond;
        if (this.absCharacter.getObjectUUID()
                < looterItems.absCharacter.getObjectUUID()) {
            lockFirst = this;
            lockSecond = looterItems;
        } else {
            lockFirst = looterItems;
            lockSecond = this;
        }

        synchronized (lockFirst) {
            synchronized (lockSecond) {
                // make sure current player has item in inventory
                if (lootItem.getItemBase().getType().equals(ItemType.GOLD) && lootItem.getObjectUUID() != this.getGoldInventory().getObjectUUID() && !(this.absCharacter.getObjectType().equals(GameObjectType.Mob)))
                    return null;
                else if (!this.inventory.contains(lootItem) && !this.getEquippedList().contains(lootItem) && !lootItem.getItemBase().getType().equals(ItemType.GOLD))
                    return null;

                // get weight of item
                ItemBase ib = lootItem.getItemBase();
                if (ib == null)
                    return null;
                short weight = ib.getWeight();

                // make sure lootingPlayer has room for item
                if (!lootItem.getItemBase().getType().equals(ItemType.GOLD) && !looterItems.hasRoomInventory(weight))
                    return null;

                if (lootItem.getItemBase().getType().equals(ItemType.GOLD))
                    if (amount != -1) { //from steal
                        int total = lootItem.getNumOfItems();
                        amount = (amount > total) ? total : amount;
                        if (!looterItems.moveGoldToInventory(lootItem, amount))
                            return null;
                        if (mobLoot != null && amount == total)
                            this.delete(mobLoot);
                    } else { //from loot
                        if (!looterItems.moveGoldToInventory(lootItem, lootItem.getNumOfItems()))
                            return null;
                        if (mobLoot != null) // delete mobloot after it has been looted
                            this.delete(mobLoot);
                    }
                else {  //not Gold item
                    boolean created = false;
                    if (mobLoot != null) {
                        lootItem = mobLoot.promoteToItem(lootingPlayer);

                        // delete mobloot after it has been looted
                        this.delete(mobLoot);
                        if (lootItem == null)
                            return null;

                        created = true;
                    }

                    // attempt to transfer item in db

                    if (!lootItem.moveItemToInventory(lootingPlayer))
                        return null;

                    // db transfer successfull, remove from this character
                    // skip this check if this is a mobLoot item (which is not in any inventory)
                    if (mobLoot == null)
                        if (!removeItemFromInventory(lootItem))
                            return null;

                    // add item to lootingPlayer.
                    if (!looterItems.addItemToInventory(lootItem))
                        return null;
                }
            }
        }

        // calculate new weights
        calculateInventoryWeight();
        looterItems.calculateInventoryWeight();

        return lootItem;
    }

    private synchronized void remItemFromLists(Item i, byte slot) {

        this.equipped.remove((int) slot);
        this.vault.remove(i);
        this.bank.remove(i);
        this.inventory.remove(i);
    }

    /*
     * Delegates
     */
    public synchronized boolean bankContains(Item i) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            return (this.getGoldBank() != null && this.goldBank.getObjectUUID() == i.getObjectUUID());
        return bank.contains(i);
    }

    public synchronized boolean inventoryContains(Item i) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            return (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == i.getObjectUUID());
        return inventory.contains(i);
    }

    public synchronized boolean forgeContains(Item i, NPC vendor) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            return (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == i.getObjectUUID());
        return vendor.getRolling().contains(i);
    }

    public synchronized boolean vaultContains(Item i) {
        if (i.getItemBase().getType().equals(ItemType.GOLD))
            return (this.getGoldVault() != null && this.goldVault.getObjectUUID() == i.getObjectUUID());
        return this.account.getVault().contains(i);
    }

    public synchronized boolean vaultContainsType(ItemBase ib) {
        if (ib.getUUID() == 7)
            return (this.getGoldVault() != null);
        for (Item i : vault) {
            if (i.getItemBase().getUUID() == ib.getUUID())
                return true;
        }
        return false;
    }

    //for calling from devCmd fill vault. Already synchronized
    public boolean vaultContainsTypeA(ItemBase ib) {
        if (ib.getUUID() == 7)
            return (this.getGoldVault() != null);
        for (Item i : vault) {
            if (i.getItemBase().getUUID() == ib.getUUID())
                return true;
        }
        return false;
    }

    public synchronized boolean equippedContains(Item i) {
        return equipped.containsValue(i);
    }

    public synchronized Item getItemFromEquipped(int slot) {
        return equipped.get(slot);
    }

    public synchronized Item getItemByUUID(int objectUUID) {
        if (this.itemIDtoType.containsKey(objectUUID)) {

            Integer integer = this.itemIDtoType.get(objectUUID);
            if (integer == GameObjectType.Item.ordinal()) {
                return Item.getFromCache(objectUUID);
            } else if (integer == GameObjectType.MobLoot.ordinal()) {
                return MobLoot.getFromCache(objectUUID);
            }

        }

        if (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == objectUUID)
            return this.goldInventory;
        if (this.getGoldBank() != null && this.goldBank.getObjectUUID() == objectUUID)
            return this.goldBank;
        if (this.getGoldVault() != null && this.goldVault.getObjectUUID() == objectUUID)
            return this.goldVault;
        return null;
    }

    public boolean tradingContains(Item i) {
        if (this.trading == null || i == null)
            return false;
        return this.trading.contains(i.getObjectUUID());
    }

    public boolean isBankOpen() {
        return this.bankOpened;
    }

    public synchronized void setBankOpen(boolean bankOpened) {
        this.bankOpened = bankOpened;
    }

    public boolean isVaultOpen() {
        return this.vaultOpened;
    }

    public synchronized void setVaultOpen(boolean vaultOpened) {
        this.vaultOpened = vaultOpened;
    }

    public ClientConnection getTradingWith() {
        return tradingWith;
    }

    public synchronized void setTradingWith(ClientConnection tradingWith) {
        this.tradingWith = tradingWith;
    }

    public synchronized void clearTradingWith() {
        this.tradingWith = null;
    }

    public int getGoldTrading() {
        return goldTradingAmount;
    }

    public byte getTradeCommitted() {
        return tradeCommitted;
    }

    public synchronized void setTradeCommitted(byte tradeCommitted) {
        this.tradeCommitted = tradeCommitted;
    }

    public HashSet<Integer> getTrading() {
        return trading;
    }

    /*
     * List Copiers
     */

    public synchronized void addItemToTrade(Item i) {
        this.trading.add(i.getObjectUUID());
    }

    public boolean getTradeSuccess() {
        return tradeSuccess;
    }

    public synchronized void setTradeSuccess(boolean tradeSuccess) {
        this.tradeSuccess = tradeSuccess;
    }

    public synchronized boolean RemoveEquipmentFromLackOfSkill(PlayerCharacter pc, boolean initialized) {


        if (pc == null)
            return false;

        if (this.equipped == null)
            return false;


        for (int slot : this.equipped.keySet()) {

            if (slot == MBServerStatics.SLOT_HAIRSTYLE || slot == MBServerStatics.SLOT_BEARDSTYLE)
                continue;

            Item item = this.equipped.get(slot);

            if (item == null) {
                this.equipped.remove(slot);
                pc.applyBonuses();
                continue;
            }

            if (!item.getItemBase().validForSkills(pc.getSkills())) {
                this.forceToInventory(slot, item, pc, initialized);
                pc.applyBonuses();
            }
        }

        return true;
    }

    /**
     * Note that this method returns a <b>copy</b> of the internally stored
     * list.
     *
     * @return the equipped
     */
    public ConcurrentHashMap<Integer, Item> getEquipped() {
        synchronized (this.equipped) {
            return new ConcurrentHashMap<>(this.equipped);
        }
    }

    public ArrayList<Item> getEquippedList() {
        ArrayList<Item> ret = new ArrayList<>();
        synchronized (this.equipOrder) {
            synchronized (this.equipped) {
                for (int slot : this.equipOrder) {
                    if (this.equipped.containsKey(slot))
                        ret.add(this.equipped.get(slot));
                }
                if (ret.size() != this.equipped.size())
                    //missed adding some items, figure out what.
                    for (int slot : this.equipped.keySet()) {
                        if (!(this.equipOrder.contains(slot))) {
                            this.equipOrder.add(slot);
                            ret.add(this.equipped.get(slot));
                        }
                    }
            }
        }
        return ret;
    }

    public Item getEquipped(int slot) {
        synchronized (this.equipped) {
            return this.equipped.get(slot);
        }
    }

    /**
     * Note that this method returns a <b>copy</b> of the internally stored
     * list.
     *
     * @return the inventory
     */
    public ArrayList<Item> getInventory() {
        return getInventory(false);
    }

    public ArrayList<Item> getInventory(boolean sendGold) {
        synchronized (this.inventory) {
            ArrayList<Item> ret = new ArrayList<>(this.inventory);
            if (sendGold && this.getGoldInventory() != null && this.goldInventory.getNumOfItems() > 0)
                ret.add(this.goldInventory);
            return ret;
        }
    }

    public int getInventoryCount() {
        synchronized (this.inventory) {
            return this.inventory.size();
        }
    }

    /**
     * Clears ownership of items. Called when player dies, but before
     * respawning.
     *
     * @return the inventory
     */
    public synchronized void orphanInventory() {
        PlayerCharacter pc = null;
        if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
            pc = (PlayerCharacter) this.absCharacter;
        synchronized (this.inventory) {
            //dupe check, validate player properly owns all items
            if (pc != null) {
                Iterator<Item> iter = this.inventory.iterator();
                while (iter.hasNext()) {
                    Item item = iter.next();
                    //this call may remove the item from this.inventory
                    if (!item.validForInventory(pc.getClientConnection(), pc, this)) {
                    }
                }
            }

            if (this.inventory.size() > 0)
                DbManager.ItemQueries.ORPHAN_INVENTORY(this.inventory);
            //make a copy of gold inventory for looting
            //so we don't remove the goldInventory
            if (this.getGoldInventory().getNumOfItems() > 0) {
                int amt = this.goldInventory.getNumOfItems();
                if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) {
                    this.goldInventory.setNumOfItems(0);
                    MobLoot gold = new MobLoot(this.absCharacter, amt);
                    this.inventory.add(gold);
                }
            }
        }
    }

    /**
     * This transfers the entire inventory to another list For populating
     * corpse' inventory when player dies
     *
     * @return the inventory
     */
    public synchronized void transferEntireInventory(
            ArrayList<Item> newInventory, Corpse corpse, boolean enterWorld) {

        PlayerCharacter pc = null;
        if (this.absCharacter != null && this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
            pc = (PlayerCharacter) this.absCharacter;

        if (this.getGoldInventory().getNumOfItems() > 0) {
            int amt = this.goldInventory.getNumOfItems();
            if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) {
                this.goldInventory.setNumOfItems(0);
                MobLoot gold = new MobLoot(this.absCharacter, amt);
                newInventory.add(gold);
            }
        }

        for (Item item : this.inventory) {
            if (item != null)
                if (item instanceof MobLoot) {

                    //MobLoot
                    item.zeroItem();
                    item.containerType = Enum.ItemContainerType.INVENTORY;

                    if (item.getItemBase().getType().equals(ItemType.GOLD))
                        //only add gold item once
                        if (!corpse.hasGold())
                            corpse.setHasGold(true);
                    newInventory.add(item);
                } else //item
                    if (item.getItemBase().getType().equals(ItemType.GOLD)) {
                        int amt = item.getNumOfItems();
                        item.setNumOfItems(0);
                        MobLoot ml = new MobLoot(this.absCharacter, amt);
                        ml.zeroItem();
                        ml.containerType = Enum.ItemContainerType.INVENTORY;
                        if (!corpse.hasGold()) {
                            corpse.setHasGold(true);
                            newInventory.add(ml);
                        }
                    } else {
                        boolean transferred = item.moveItemToInventory(corpse);
                        if (!transferred)
                            Logger.error(
                                    "CharItemManager.transferEntireInvetory",
                                    "DB Error, Failed to transfer item "
                                            + item.getObjectUUID() + " to new owner "
                                            + corpse.getObjectUUID());
                        newInventory.add(item);

                    }
        }

        // tell client we're clearing inventory


        // clear the inventory.
        this.inventory.clear();

        //re-calculate inventory weight
        calculateInventoryWeight();
        if (!enterWorld)
            updateInventory(this.getInventory(), false);
    }

    public synchronized void purgeInventory() {

        if (!this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter))
            return;

        if (this.goldInventory != null)
            if (this.getGoldInventory().getNumOfItems() > 0) {
                if (DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) {
                    this.goldInventory.setNumOfItems(0);
                }
            }

        if (this.inventory.size() > 0)
            DbManager.ItemQueries.ORPHAN_INVENTORY(this.inventory);

        // clear the inventory.
        this.inventory.clear();
        //re-calculate inventory weight
        calculateInventoryWeight();
    }

    /**
     * Note that this method returns a <b>copy</b> of the internally stored
     * list.
     *
     * @return the bank
     */
    public ArrayList<Item> getBank() {
        synchronized (this.bank) {
            ArrayList<Item> ret = new ArrayList<>(this.bank);
            if (this.getGoldBank() != null && this.goldBank.getNumOfItems() > 0)
                ret.add(this.goldBank);
            return ret;
        }
    }

    /**
     * Note that this method returns a <b>copy</b> of the internally stored
     * list.
     *
     * @return the vault
     */
    public ArrayList<Item> getVault() {
        synchronized (this.vault) {
            ArrayList<Item> ret = new ArrayList<>(this.vault);
            if (this.getGoldVault() != null && this.goldVault.getNumOfItems() > 0)
                ret.add(this.goldVault);
            return ret;
        }
    }

    public boolean hasRoomInventory(short weight) {
        if (this.absCharacter == null)
            return false;
        if (this.absCharacter.getObjectType() == GameObjectType.PlayerCharacter) {
            PlayerCharacter pc = (PlayerCharacter) this.absCharacter;
            int newWeight = this.getCarriedWeight() + weight;
            return newWeight <= (int) pc.statStrBase * 3;
        } else if (this.absCharacter.getObjectType() == GameObjectType.NPC) {
            int newWeight = this.getCarriedWeight() + weight;
            return newWeight <= 1900 + (this.absCharacter.getLevel() * 3);
        } else
            return true; // npc's need checked
    }

    public boolean hasRoomTrade(short itemWeight) {

        PlayerCharacter playerCharacter;
        PlayerCharacter tradeCharacter;

        int tradeWeight;

        if (this.absCharacter == null)
            return false;

        if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false)
            return false;

        playerCharacter = (PlayerCharacter) this.absCharacter;

        if ((this.tradingWith == null) ||
                (this.tradingWith.isConnected() == false))
            return false;

        tradeCharacter = this.tradingWith.getPlayerCharacter();

        tradeWeight = this.getCarriedWeight() + itemWeight;
        tradeWeight = tradeWeight + tradeCharacter.getCharItemManager().getTradingWeight();
        tradeWeight = tradeWeight - this.getTradingWeight();

        return tradeWeight <= (int) playerCharacter.statStrBase * 3;
    }

    public boolean hasRoomBank(short weight) {
        if (this.absCharacter == null)
            return false;
        return weight <= this.absCharacter.getBankCapacityRemaining();
    }

    public boolean hasRoomVault(short weight) {
        if (this.absCharacter == null)
            return false;
        return weight <= this.absCharacter.getVaultCapacityRemaining();
    }

    public int getCarriedWeight() {
        return getInventoryWeight() + getEquipWeight();
    }

    public int getInventoryWeight() {
        return this.inventoryWeight;
    }

    public int getBankWeight() {
        return this.bankWeight;
    }

    public int getEquipWeight() {
        return this.equipWeight;
    }

    public int getVaultWeight() {
        return this.vaultWeight;
    }

    public int getTradingForWeight() {
        return calculateTradingForWeight();
    }

    public int getTradingWeight() {

        int weight = 0;
        Item item;

        for (int i : this.trading) {
            item = Item.getFromCache(i);

            if (item == null)
                continue;

            ItemBase ib = item.getItemBase();
            weight += ib.getWeight();
        }
        return weight;
    }

    public AbstractCharacter getOwner() {
        return this.absCharacter;
    }

    public void calculateWeights() {
        calculateBankWeight();
        calculateInventoryWeight();
        calculateEquipWeight();
        calculateVaultWeight();
    }

    public void calculateBankWeight() {
        this.bankWeight = 0;
        for (Item i : this.bank) {
            ItemBase ib = i.getItemBase();
            if (ib != null)
                this.bankWeight += ib.getWeight();
        }
    }

    public void calculateEquipWeight() {
        this.equipWeight = 0;
        Collection<Item> c = this.equipped.values();
        Iterator<Item> it = c.iterator();
        while (it.hasNext()) {
            Item i = it.next();
            ItemBase ib = i.getItemBase();
            if (ib != null)
                this.equipWeight += ib.getWeight();
        }
    }

    public void calculateInventoryWeight() {
        this.inventoryWeight = 0;
        for (Item i : this.inventory) {
            ItemBase ib = i.getItemBase();
            if (ib != null)
                this.inventoryWeight += ib.getWeight();
        }
    }

    public void calculateVaultWeight() {
        this.vaultWeight = 0;
        for (Item i : this.vault) {
            ItemBase ib = i.getItemBase();
            if (ib != null)
                this.vaultWeight += ib.getWeight();
        }
    }

    private int calculateTradingForWeight() {
        int tradingForWeight = 0;

        return tradingForWeight;
    }

    public void updateInventory(Item item, boolean add) {
        ArrayList<Item> list = new ArrayList<>();
        list.add(item);
        updateInventory(list, add);
    }

    private void updateInventory(ArrayList<Item> inventory, boolean add) {

        if (this.absCharacter == null)
            return;

        if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false)
            return;

        PlayerCharacter pc = (PlayerCharacter) this.absCharacter;

        UpdateInventoryMsg updateInventoryMsg = new UpdateInventoryMsg(inventory, this.getBank(), this.getGoldInventory(), add);
        Dispatch dispatch = Dispatch.borrow(pc, updateInventoryMsg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

    }

    public void forceToInventory(int slot, Item item, PlayerCharacter pc, boolean initialized) {
        if (item == null || pc == null)
            return;

        if (!item.moveItemToInventory(pc)) {
            //TODO well why did this fail? clean it up
        }

        // remove it from other lists:
        this.remItemFromLists(item, (byte) slot);

        // add to Inventory
        this.inventory.add(item);
        item.addToCache();

        calculateWeights();

        //Update players with unequipped item
        if (initialized) {
            TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(pc, slot);
            DispatchMessage.dispatchMsgToInterestArea(pc, back, engine.Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
        }

    }

    /**
     * Update the player's inventory window by resending the entire contents.
     */
    public void updateInventory() {
        this.updateInventory(this.getInventory(), true);
    }

    public synchronized void initializeTrade() {
        this.trading = new HashSet<>();
    }

    public synchronized boolean commitTrade() {
        int goldFrom1 = 0;
        int goldFrom2 = 0;

        if (this.getTradingWith() == null || this.getTradingWith().isConnected() == false
                || this.getTradingWith().getPlayerCharacter() == null) {
            this.endTrade();
            return false;
        }


        CharacterItemManager tradingWith = this.getTradingWith().getPlayerCharacter().getCharItemManager();

        if (tradingWith == null)
            return false;

        if (this.goldTradingAmount != 0) {

            if (tradingWith.goldInventory == null) {
                Logger.error("Null Gold for player " + this.getOwner().getObjectUUID());
                return false;
            }
            goldFrom1 = this.goldTradingAmount;
        }
        if (tradingWith.goldTradingAmount != 0) {

            if (this.getGoldInventory() == null) {
                Logger.error("Null Gold for player " + this.getOwner().getObjectUUID());
                return false;
            }
            goldFrom2 = tradingWith.goldTradingAmount;
        }


        if (this.getGoldInventory().getNumOfItems() + goldFrom2 > 10000000) {
            PlayerCharacter pc = (PlayerCharacter) this.absCharacter;
            if (pc.getClientConnection() != null)
                ErrorPopupMsg.sendErrorPopup(pc, 202);
            return false;
        }


        if (tradingWith.getGoldInventory().getNumOfItems() + goldFrom1 > 10000000) {
            PlayerCharacter pc = (PlayerCharacter) tradingWith.absCharacter;
            if (pc.getClientConnection() != null)
                ErrorPopupMsg.sendErrorPopup(pc, 202);
            return false;
        }

        if (this.trading.size() > 0 || tradingWith.trading.size() > 0 || goldFrom1 > 0 || goldFrom2 > 0) {
            if (!DbManager.ItemQueries.DO_TRADE(this.trading, tradingWith.trading, this, tradingWith,
                    this.goldInventory, tradingWith.goldInventory, goldFrom1, goldFrom2))
                return false;
        } else
            return true;

        for (int i : this.trading) {
            Item item = Item.getFromCache(i);
            if (item == null)
                continue;
            this.trade(item);
            tradingWith.tradeForItem(item);
        }
        for (int i : tradingWith.trading) {
            Item item = Item.getFromCache(i);
            if (item == null)
                continue;
            tradingWith.trade(item);
            this.tradeForItem(item);
        }

        //subtract gold your trading from your inventory.
        if (this.goldTradingAmount > 0)
            this.getGoldInventory().setNumOfItems(this.getGoldInventory().getNumOfItems() - this.goldTradingAmount);
        //subtract gold your trading from your inventory.
        if (tradingWith.goldTradingAmount > 0)
            tradingWith.getGoldInventory().setNumOfItems(tradingWith.getGoldInventory().getNumOfItems() - tradingWith.goldTradingAmount);

        if (tradingWith.goldTradingAmount > 0)
            this.getGoldInventory().setNumOfItems(this.goldInventory.getNumOfItems()
                    + tradingWith.goldTradingAmount);
        if (this.goldTradingAmount > 0)
            tradingWith.getGoldInventory().setNumOfItems(tradingWith.goldInventory.getNumOfItems()
                    + this.goldTradingAmount);

        this.tradeSuccess = true;
        tradingWith.tradeSuccess = true;

        return true;

    }

    public synchronized void endTrade() {
        updateInventory(this.getInventory(), true);
        this.tradeCommitted = (byte) 0;
        this.tradeSuccess = false;
        this.tradingWith = null;
        this.trading = null;
        this.goldTradingAmount = 0;
        this.tradeID = 0;
    }

    public synchronized void endTrade(boolean fromDeath) {
        this.tradeCommitted = (byte) 0;
        this.tradeSuccess = false;
        this.tradingWith = null;
        this.trading = null;
        this.goldTradingAmount = 0;
    }

    // Remove item from your possession
    private synchronized boolean trade(Item i) {
        if (this.doesCharOwnThisItem(i.getObjectUUID()) == false)
            return false;

        // Only valid from inventory
        if (!inventoryContains(i))
            return false;

        // remove from Inventory
        this.inventory.remove(i);
        this.itemIDtoType.remove(i.getObjectUUID());
        i.setOwnerID(0);

        calculateWeights();
        stripTempEnchants(i);
        return true;
    }

    //Damage an equipped item a specified amount
    public void damageItem(Item item, int amount) {
        if (item == null || amount < 1 || amount > 5)
            return;

        if(ZoneManager.findSmallestZone(this.getOwner().loc).getSafeZone() == 1){
            return;
        }

        //verify the item is equipped by this player
        int slot = item.getEquipSlot();
        if (!this.equipped.containsKey(slot))
            return;
        Item verify = this.equipped.get(slot);
        if (verify == null || item.getObjectUUID() != verify.getObjectUUID())
            return;

        //don't damage noob gear, hair or beards.
        if (item.getDurabilityMax() == 0)
            return;

        if (!item.isCanDestroy())
            return;

        int dur = (int) item.getDurabilityCurrent();
        if (dur - amount <= 0) {
            //destroy the item
            junk(item);

            //TODO remove item from the client
            //This may not be correct
            dur = 0;
        } else {
            dur -= amount;
            if (!DbManager.ItemQueries.SET_DURABILITY(item, dur))
                return;
            item.setDurabilityCurrent((short) dur);

        }

        if (this.absCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) == false)
            return;

        //send damage item msg to client
        PlayerCharacter pc = (PlayerCharacter) this.absCharacter;

        ItemHealthUpdateMsg itemHealthUpdateMsg = new ItemHealthUpdateMsg(slot, (float) dur);
        Dispatch dispatch = Dispatch.borrow(pc, itemHealthUpdateMsg);
        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);

    }

    //Damage a random piece of armor a specified amount
    public void damageRandomArmor(int amount) {
        ArrayList<Item> armor = new ArrayList<>();
        if (this.equipped.containsKey(MBServerStatics.SLOT_OFFHAND)) {
            Item item = this.equipped.get(MBServerStatics.SLOT_OFFHAND);
            ItemBase ib = item.getItemBase();
            if (ib.isShield())
                armor.add(item);
        }
        if (this.equipped.containsKey(MBServerStatics.SLOT_HELMET))
            armor.add(this.equipped.get(MBServerStatics.SLOT_HELMET));
        if (this.equipped.containsKey(MBServerStatics.SLOT_CHEST))
            armor.add(this.equipped.get(MBServerStatics.SLOT_CHEST));
        if (this.equipped.containsKey(MBServerStatics.SLOT_ARMS))
            armor.add(this.equipped.get(MBServerStatics.SLOT_ARMS));
        if (this.equipped.containsKey(MBServerStatics.SLOT_GLOVES))
            armor.add(this.equipped.get(MBServerStatics.SLOT_GLOVES));
        if (this.equipped.containsKey(MBServerStatics.SLOT_GLOVES))
            armor.add(this.equipped.get(MBServerStatics.SLOT_GLOVES));
        if (this.equipped.containsKey(MBServerStatics.SLOT_LEGGINGS))
            armor.add(this.equipped.get(MBServerStatics.SLOT_LEGGINGS));
        if (this.equipped.containsKey(MBServerStatics.SLOT_FEET))
            armor.add(this.equipped.get(MBServerStatics.SLOT_FEET));

        if (armor.isEmpty())
            return; //nothing to damage

        int roll = ThreadLocalRandom.current().nextInt(armor.size());
        damageItem(armor.get(roll), amount);
    }

    //Damage all equipped gear a random amount between 1 and 5
    public void damageAllGear() {
        for (Item gear : this.equipped.values()) {
            damageItem(gear, (ThreadLocalRandom.current().nextInt(5) + 1));
        }
    }

    // Add item to your possession
    public synchronized boolean tradeForItem(Item i) {
        // add to Inventory
        this.inventory.add(i);
        this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());
        i.setOwnerID(this.absCharacter.getObjectUUID());

        calculateWeights();

        return true;
    }

    public synchronized boolean addGoldToTrade(int amount) {

        if (this.goldTradingAmount + amount > MBServerStatics.PLAYER_GOLD_LIMIT)
            return false;

        this.goldTradingAmount += amount;

        return true;
    }

    /**
     * Completely empties inventory, deleting any items. Use with caution!
     */
    public synchronized void clearInventory() {
        this.getGoldInventory().setNumOfItems(0);
        Iterator<Item> ii = this.inventory.iterator();
        while (ii.hasNext()) {
            Item itm = ii.next();
            ii.remove();
            this.delete(itm);
        }
    }

    public synchronized void clearEquip() {

        ArrayList<Item> equipCopy = new ArrayList<>(this.getEquippedList());
        Iterator<Item> ii = equipCopy.iterator();
        while (ii.hasNext()) {
            Item itm = ii.next();
            this.getEquippedList().remove(itm);
            this.delete(itm);
        }
    }

    public byte getEquipVer() {
        return this.equipVer;
    }

    public void incEquipVer() {
        this.equipVer++;
    }

    public void incInventoryVer() {
        this.equipVer++;
    }

    public void incBankVer() {
        this.equipVer++;
    }

    public void incVaultVer() {
        this.equipVer++;
    }

    public int getTradeID() {
        return tradeID;
    }

    public synchronized boolean closeTradeWindow() {
        if (this.getTradingWith() != null || this.getTradeID() != 0)
            this.closeTradeWindow(new CloseTradeWindowMsg(this.getOwner(), this.getTradeID()), true);
        return true;

    }

}