// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.net.client.handlers; import engine.exception.MsgSendException; import engine.gameManager.ChatManager; import engine.gameManager.DbManager; import engine.gameManager.ForgeManager; import engine.gameManager.ItemManager; import engine.loot.WorkOrder; import engine.mbEnums; import engine.mbEnums.ItemType; import engine.net.Dispatch; import engine.net.DispatchMessage; import engine.net.client.ClientConnection; import engine.net.client.msg.ClientNetMsg; import engine.net.client.msg.ErrorPopupMsg; import engine.net.client.msg.ItemProductionMsg; import engine.net.client.msg.ManageNPCMsg; import engine.objects.*; import org.pmw.tinylog.Logger; import java.util.HashMap; /* * @Summary: Processes application protocol message which modifies * hireling inventory through rolling, junking or depositing. */ public class ItemProductionMsgHandler extends AbstractClientMsgHandler { public ItemProductionMsgHandler() { super(); } @Override protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { // Member variable declaration PlayerCharacter player; NPC vendor; ItemProductionMsg msg; Dispatch dispatch; // Member variable assignment msg = (ItemProductionMsg) baseMsg; player = origin.getPlayerCharacter(); if (player == null) return true; // Grab reference to vendor we are interacting with vendor = (NPC) DbManager.getObject(mbEnums.GameObjectType.NPC, msg.npcUUID); // Oops? if (vendor == null) return true; // Process Request switch (msg.actionType) { case PRODUCE: // Create new work order WorkOrder workOrder = new WorkOrder(); workOrder.total_to_produce = msg.total_to_produce; workOrder.vendor = vendor; workOrder.templateID = msg.itemUUID; workOrder.prefixToken = msg.pToken; workOrder.suffixToken = msg.sToken; workOrder.item_name_override = msg.name; workOrder.multiple_slot_request = (msg.size != 0); // Submit workOder to begin rolling items int validation_result = ForgeManager.submit(workOrder); // workOrder could not be completed // Report back to the user the reason why if (validation_result != 0) { ErrorPopupMsg.sendErrorPopup(player, validation_result); return true; } break; case JUNK: junkItem(msg.itemUUID, vendor, origin); break; case RECYCLE: recycleItem(msg.items, vendor, origin); msg.actionType = mbEnums.ProductionActionType.TAKE; dispatch = Dispatch.borrow(player, msg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); break; case COMPLETE: completeItem(msg.itemUUID, vendor); break; case DEPOSIT: depositItem(msg.itemUUID, vendor, origin); break; case SETPRICE: setItemPrice(msg.itemUUID, msg.itemPrice, vendor, origin); break; case TAKE: takeItem(msg.items, vendor, origin); dispatch = Dispatch.borrow(player, msg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); break; } return true; } private static void completeItem(int itemUUID, NPC vendor) { Item virtualItem = Item.getFromCache(itemUUID); WorkOrder workOrder = ForgeManager.itemWorkOrderLookup.get(virtualItem); City city = workOrder.vendor.building.getCity(); if (city == null) return; city.transactionLock.writeLock().lock(); try { // Remove virtual item from the vendor rolling window // (Add / Completed / Remove) ItemProductionMsg outMsg = new ItemProductionMsg(workOrder.vendor.building, workOrder.vendor, virtualItem, mbEnums.ProductionActionType.CONFIRM_SETPRICE, true); DispatchMessage.dispatchMsgToInterestArea(workOrder.vendor, outMsg, mbEnums.DispatchChannel.SECONDARY, 700, false, false); // Remove virtualItem from collections workOrder.cooking.remove(virtualItem); ForgeManager.itemWorkOrderLookup.remove(virtualItem); DbManager.removeFromCache(virtualItem); workOrder.slots_used.decrementAndGet(); // Update workOrder on disk if (workOrder.cooking.isEmpty()) { ForgeManager.vendorWorkOrderLookup.get(vendor).remove(workOrder); DbManager.WarehouseQueries.DELETE_WORKORDER(workOrder); } else DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder); // Persist item and add to vendor inventory virtualItem.containerType = mbEnums.ItemContainerType.INVENTORY; Item completedItem = DbManager.ItemQueries.PERSIST(virtualItem); // Apply Item effects for Prefix and Suffix tokens completedItem.prefixToken = virtualItem.prefixToken; completedItem.suffixToken = virtualItem.suffixToken; ItemManager.applyItemEffects(completedItem); vendor.charItemManager.addItemToInventory(completedItem); // Add persisted items to the vendor inventory window ItemProductionMsg outMsg1 = new ItemProductionMsg(vendor.building, vendor, completedItem, mbEnums.ProductionActionType.DEPOSIT, true); DispatchMessage.dispatchMsgToInterestArea(vendor, outMsg1, mbEnums.DispatchChannel.SECONDARY, 700, false, false); ItemProductionMsg outMsg2 = new ItemProductionMsg(vendor.building, vendor, completedItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT, true); DispatchMessage.dispatchMsgToInterestArea(vendor, outMsg2, mbEnums.DispatchChannel.SECONDARY, 700, false, false); } catch (Exception e) { Logger.error(e); } finally { city.transactionLock.writeLock().unlock(); } } private static void setItemPrice(int itemUUID, int itemPrice, NPC vendor, ClientConnection origin) { Item targetItem; ItemProductionMsg outMsg; Dispatch dispatch; PlayerCharacter player = origin.getPlayerCharacter(); if (player == null) return; targetItem = Item.getFromCache(itemUUID); if (targetItem == null) return; if (!DbManager.ItemQueries.UPDATE_VALUE(targetItem, itemPrice)) { ChatManager.chatInfoError(origin.getPlayerCharacter(), "Failed to set price! Contact CCR For help."); return; } targetItem.setValue(itemPrice); targetItem.magicValue = targetItem.value; outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.DEPOSIT, true); dispatch = Dispatch.borrow(player, outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.SETPRICE, true); dispatch = Dispatch.borrow(player, outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); } // Method sets the price on an item in the vendor inventory private static void depositItem(int itemUUID, NPC vendor, ClientConnection origin) { Item targetItem; ItemProductionMsg outMsg; CharacterItemManager itemMan; Dispatch dispatch; PlayerCharacter player = origin.getPlayerCharacter(); if (player == null) return; if (origin.sellLock.tryLock()) { try { targetItem = Item.getFromCache(itemUUID); if (targetItem == null) return; if (targetItem.template.item_type.equals(ItemType.GOLD)) return; if (!vendor.charItemManager.hasRoomInventory(targetItem.template.item_wt)) { ErrorPopupMsg.sendErrorPopup(player, 21); return; } itemMan = origin.getPlayerCharacter().charItemManager; if (itemMan == null) return; if (vendor.charItemManager.getInventoryWeight() > 500) { ErrorPopupMsg.sendErrorPopup(player, 21); return; } if (!targetItem.validForInventory(origin, player, itemMan)) { ErrorPopupMsg.sendErrorPopup(player, 19); return; } // Transfer item from player to vendor's inventory if (!itemMan.sellToNPC(targetItem, vendor)) { ErrorPopupMsg.sendErrorPopup(player, 109); return; } outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.DEPOSIT, true); dispatch = Dispatch.borrow(player, outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT, true); dispatch = Dispatch.borrow(player, outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); origin.getPlayerCharacter().charItemManager.updateInventory(); } catch (Exception e) { Logger.error(e); } finally { origin.sellLock.unlock(); } } } // Method completes an item that has been previously rolled // adding it to the Vendor inventory private static void recycleItem(HashMap itemList, NPC vendor, ClientConnection origin) { Item targetItem; ItemProductionMsg outMsg; int totalValue = 0; Dispatch dispatch; if (vendor.getBuilding() == null) return; PlayerCharacter player = origin.getPlayerCharacter(); if (player == null) return; if (itemList == null) return; if (origin.sellLock.tryLock()) { try { for (int itemUUID : itemList.keySet()) { int itemValue = 0; targetItem = Item.getFromCache(itemUUID); if (targetItem == null) continue; if (targetItem.template.item_type.equals(ItemType.GOLD)) return; if (!vendor.charItemManager.doesCharOwnThisItem(targetItem.getObjectUUID())) continue; if (!vendor.charItemManager.inventoryContains(targetItem)) continue; itemValue = targetItem.template.item_value; if (vendor.getBuilding().getStrongboxValue() + itemValue > vendor.getBuilding().getMaxGold()) { ErrorPopupMsg.sendErrorPopup(player, 201); break; } switch (targetItem.template.item_type) { case EMPLOYMENTCONTRACT: case CHARTER: case DEED: case REALMCHARTER: case SCROLL: case POTION: continue; } totalValue += itemValue; vendor.charItemManager.recycle(targetItem); outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.TAKE, true); dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); } // Refund a portion of the gold vendor.getBuilding().transferGold(totalValue, false); } catch (Exception e) { Logger.error(e); } finally { origin.sellLock.unlock(); } } } // Method handles recycling of an item private static void junkItem(int itemUUID, NPC vendor, ClientConnection origin) { Item virtualItem; ManageNPCMsg outMsg; Dispatch dispatch; PlayerCharacter player = origin.getPlayerCharacter(); if (player == null) return; // Cannot junk items without a forge! if (vendor.getBuilding() == null) return; // junk nothing? virtualItem = Item.getFromCache(itemUUID); if (virtualItem == null) return; WorkOrder workOrder = ForgeManager.itemWorkOrderLookup.get(virtualItem); // If this virtual item was already processed then // it will have been removed from the workOrder. if (workOrder == null) return; if (!workOrder.cooking.contains(virtualItem)) return; City city = workOrder.vendor.building.getCity(); if (city == null) return; city.transactionLock.writeLock().lock(); try { workOrder.cooking.remove(virtualItem); DbManager.removeFromCache(virtualItem); ForgeManager.itemWorkOrderLookup.remove(virtualItem); // Update total_to_produce accounting for the slot being // removed while workOrder is not completed. if (!workOrder.runCompleted.get()) if (workOrder.multiple_slot_request && workOrder.slots_used.get() > 1) { int itemsPerSlot = workOrder.total_to_produce / workOrder.slots_used.get(); workOrder.total_to_produce = workOrder.total_to_produce - itemsPerSlot; } // Slot is no longer allocated to this workOrder. workOrder.slots_used.decrementAndGet(); // Update workOrder on disk if (workOrder.cooking.isEmpty()) { ForgeManager.vendorWorkOrderLookup.get(vendor).remove(workOrder); DbManager.WarehouseQueries.DELETE_WORKORDER(workOrder); } else DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder); // Refresh vendor's inventory to client outMsg = new ManageNPCMsg(vendor); outMsg.setMessageType(1); dispatch = Dispatch.borrow(player, outMsg); DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY); } catch (Exception e) { Logger.error(e); } finally { city.transactionLock.writeLock().unlock(); } } private static void takeItem(HashMap itemList, NPC vendor, ClientConnection origin) { Item targetItem; PlayerCharacter player = origin.getPlayerCharacter(); if (player == null) return; for (int itemUUID : itemList.keySet()) { targetItem = Item.getFromCache(itemUUID); if (targetItem == null) return; if (targetItem.template.item_type.equals(ItemType.GOLD)) return; if (!vendor.charItemManager.inventoryContains(targetItem)) return; if (!player.charItemManager.hasRoomInventory(targetItem.template.item_wt)) return; player.charItemManager.buyFromNPC(targetItem, vendor); } player.charItemManager.updateInventory(); } }