|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|