// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // 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.BuildingManager; import engine.gameManager.ChatManager; import engine.gameManager.ConfigManager; import engine.gameManager.DbManager; 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.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 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 equipped = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW); private final HashSet inventory = new HashSet<>(); private final HashSet bank = new HashSet<>(); private final HashSet vault = new HashSet<>(); private final HashSet 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 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 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 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); 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(); 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(); return true; } //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; } 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) 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 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 copy of the internally stored * list. * * @return the equipped */ public ConcurrentHashMap getEquipped() { synchronized (this.equipped) { return new ConcurrentHashMap<>(this.equipped); } } public ArrayList getEquippedList() { ArrayList 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 copy of the internally stored * list. * * @return the inventory */ public ArrayList getInventory() { return getInventory(false); } public ArrayList getInventory(boolean sendGold) { synchronized (this.inventory) { ArrayList 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 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 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 copy of the internally stored * list. * * @return the bank */ public ArrayList getBank() { synchronized (this.bank) { ArrayList 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 copy of the internally stored * list. * * @return the vault */ public ArrayList getVault() { synchronized (this.vault) { ArrayList 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 c = this.equipped.values(); Iterator 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 list = new ArrayList<>(); list.add(item); updateInventory(list, add); } private void updateInventory(ArrayList 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(); return true; } //Damage an equipped item a specified amount public void damageItem(Item item, int amount) { if (item == null || amount < 1 || amount > 5) 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 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 ii = this.inventory.iterator(); while (ii.hasNext()) { Item itm = ii.next(); ii.remove(); this.delete(itm); } } public synchronized void clearEquip() { ArrayList equipCopy = new ArrayList<>(this.getEquippedList()); Iterator 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; } }