You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
487 lines
17 KiB
487 lines
17 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// 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<Integer, Integer> 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<Integer, Integer> 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(); |
|
} |
|
|
|
}
|
|
|