Files
magicbane/src/engine/objects/Warehouse.java
T
MagicBotandClaude Opus 5.5 e64d64bf18 Keep destroyed warehouse contents in memory so the rubble can be looted
destroyOrDerank nulled city.warehouse on destruction, so the loot click
found no warehouse and returned silently. The destroyed Warehouse is now
held in Warehouse.destroyedWarehouses keyed by the rubble building UUID
until it is looted empty, the rubble is destroyed, or the city places a
new warehouse. Contents are not persisted across a reboot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 09:19:40 -04:00

634 lines
23 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.gameManager.DispatchManager;
import engine.loot.WorkOrder;
import engine.mbEnums;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONObject;
import org.pmw.tinylog.Logger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
public class Warehouse {
// Destroyed warehouses keyed by rubble building UUID; memory only so the rubble can be looted
public static final ConcurrentHashMap<Integer, Warehouse> destroyedWarehouses = new ConcurrentHashMap<>();
public EnumSet<mbEnums.ResourceType> locked = EnumSet.noneOf(mbEnums.ResourceType.class);
public Building building;
public City city;
public ArrayList<Transaction> transactions = new ArrayList<>();
public ConcurrentHashMap<mbEnums.ResourceType, Integer> resources = new ConcurrentHashMap<>();
public Warehouse(Building building) {
this.building = building;
this.city = building.getCity();
// New empty warehouse
for (mbEnums.ResourceType resourceType : EnumSet.allOf(mbEnums.ResourceType.class))
this.resources.put(resourceType, 0);
// With no locks
this.locked = EnumSet.noneOf(mbEnums.ResourceType.class);
}
public Warehouse(JSONObject warehouse) throws SQLException {
JSONObject resources = (JSONObject) warehouse.get("resources");
for (String key : resources.keySet()) {
mbEnums.ResourceType resourceType = mbEnums.ResourceType.valueOf(key);
int value = resources.getInt(key);
this.resources.put(resourceType, value);
}
JSONArray lockedResources = (JSONArray) warehouse.get("locked");
if (lockedResources.isEmpty() == false)
for (Object o : lockedResources)
this.locked.add(mbEnums.ResourceType.valueOf((String) o));
}
public static void warehouseDeposit(MerchantMsg msg, PlayerCharacter playerCharacter, NPC npc) {
Building warehouseBuilding;
Warehouse warehouse;
int depositAmount;
Dispatch dispatch;
Item resource = Item.getFromCache(msg.getItemID());
if (resource == null)
return;
depositAmount = msg.getAmount();
CharacterItemManager itemMan = playerCharacter.charItemManager;
if (!itemMan.doesCharOwnThisItem(resource.getObjectUUID()))
return;
warehouseBuilding = npc.getBuilding();
if (warehouseBuilding == null)
return;
City city = warehouseBuilding.getCity();
if (city == null)
return;
warehouse = city.warehouse;
if (warehouse == null)
return;
if (!deposit(playerCharacter, resource, depositAmount, true, true, warehouse))
return;
ViewResourcesMsg vrm = new ViewResourcesMsg(playerCharacter);
vrm.setGuild(playerCharacter.getGuild());
vrm.setWarehouseBuilding(warehouseBuilding);
vrm.configure();
dispatch = Dispatch.borrow(playerCharacter, vrm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
}
public static void warehouseWithdraw(MerchantMsg msg, PlayerCharacter playerCharacter, NPC npc) {
int withdrawAmount;
Building warehouseBuilding;
Warehouse warehouse;
Dispatch dispatch;
withdrawAmount = msg.getAmount();
warehouseBuilding = npc.getBuilding();
if (warehouseBuilding == null)
return;
if (playerCharacter.getGuild() != warehouseBuilding.getGuild() || !GuildStatusController.isInnerCouncil(playerCharacter.getGuildStatus()))
return;
City city = warehouseBuilding.getCity();
if (city == null)
return;
warehouse = city.warehouse;
if (warehouse == null)
return;
mbEnums.ResourceType resourceType = mbEnums.ResourceType.templateHashLookup.get(msg.getHashID());
if (isResourceLocked(warehouse, resourceType)) {
ChatManager.chatSystemInfo(playerCharacter, "You cannot withdrawl a locked resource.");
return;
}
if (!withdraw(warehouse, playerCharacter, resourceType, withdrawAmount, true, true)) {
ChatManager.chatGuildError(playerCharacter, "Failed to withdrawl " + resourceType.name() + '.');
Logger.debug(playerCharacter.getName() + " Failed to withdrawl =" + resourceType.name() + " from Warehouse With ID = " + warehouseBuilding.getObjectUUID());
return;
}
ViewResourcesMsg vrm = new ViewResourcesMsg(playerCharacter);
vrm.setGuild(playerCharacter.getGuild());
vrm.setWarehouseBuilding(warehouseBuilding);
vrm.configure();
dispatch = Dispatch.borrow(playerCharacter, vrm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
}
public static void warehouseLock(MerchantMsg msg, PlayerCharacter playerCharacter, NPC npc) {
Building warehouseBuilding;
Warehouse warehouse;
int hashID;
Dispatch dispatch;
hashID = msg.getHashID();
warehouseBuilding = npc.getBuilding();
if (warehouseBuilding == null)
return;
if (playerCharacter.getGuild() != warehouseBuilding.getGuild() || !GuildStatusController.isInnerCouncil(playerCharacter.getGuildStatus()))
return;
City city = warehouseBuilding.getCity();
if (city == null)
return;
warehouse = city.warehouse;
mbEnums.ResourceType resourceType = mbEnums.ResourceType.templateHashLookup.get(hashID);
// toggle lock
if (warehouse.locked.contains(resourceType)) {
boolean worked;
warehouse.locked.remove(resourceType);
worked = DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse);
if (worked) {
ViewResourcesMsg vrm = new ViewResourcesMsg(playerCharacter);
vrm.setGuild(playerCharacter.getGuild());
vrm.setWarehouseBuilding(warehouseBuilding);
vrm.configure();
dispatch = Dispatch.borrow(playerCharacter, vrm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
} else
warehouse.locked.add(resourceType);
return;
}
boolean worked;
warehouse.locked.add(resourceType);
worked = DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse);
if (worked) {
ViewResourcesMsg vrm = new ViewResourcesMsg(playerCharacter);
vrm.setGuild(playerCharacter.getGuild());
vrm.setWarehouseBuilding(warehouseBuilding);
vrm.configure();
dispatch = Dispatch.borrow(playerCharacter, vrm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
} else
warehouse.locked.remove(resourceType);
}
public static synchronized boolean deposit(PlayerCharacter playerCharacter, Item resource, int amount, boolean removeFromInventory, boolean transaction, Warehouse warehouse) {
ClientConnection origin = playerCharacter.getClientConnection();
if (origin == null)
return false;
if (amount < 0) {
Logger.info(playerCharacter.getFirstName() + " Attempting to Dupe!!!!!!");
return false;
}
mbEnums.ResourceType resourceType = mbEnums.ResourceType.templateLookup.get(resource.templateID);
if (warehouse.resources.get(resourceType) == null)
return false;
CharacterItemManager itemMan = playerCharacter.charItemManager;
if (itemMan == null)
return false;
if (itemMan.getGoldTrading() > 0) {
ErrorPopupMsg.sendErrorPopup(playerCharacter, 195);
return false;
}
if (!itemMan.doesCharOwnThisItem(resource.getObjectUUID()))
return false;
if (!resource.validForInventory(origin, playerCharacter, itemMan))
return false;
if (resource.getNumOfItems() < amount)
return false;
int oldAmount = warehouse.resources.get(resourceType);
int newAmount = oldAmount + amount;
if (newAmount > mbEnums.ResourceType.templateLookup.get(resource.templateID).deposit_limit)
return false;
if (removeFromInventory) {
if (resourceType.equals(mbEnums.ResourceType.GOLD)) {
if (itemMan.getGoldInventory().getNumOfItems() - amount < 0)
return false;
if (itemMan.getGoldInventory().getNumOfItems() - amount > MBServerStatics.PLAYER_GOLD_LIMIT)
return false;
if (!itemMan.modifyInventoryGold(-amount))
return false;
UpdateGoldMsg ugm = new UpdateGoldMsg(playerCharacter);
ugm.configure();
Dispatch dispatch = Dispatch.borrow(playerCharacter, ugm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
itemMan.updateInventory();
} else {
itemMan.delete(resource);
itemMan.updateInventory();
}
}
itemMan.updateInventory();
if (newAmount > resourceType.deposit_limit)
return false;
warehouse.resources.put(resourceType, newAmount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.put(resourceType, oldAmount);
return false;
}
if (resource.template.item_type.equals(mbEnums.ItemType.GOLD))
resourceType = mbEnums.ResourceType.GOLD;
else
resourceType = mbEnums.ResourceType.valueOf(ItemTemplate.templates.get(resource.templateID).item_base_name.toUpperCase());
if (transaction)
AddTransactionToWarehouse(warehouse, playerCharacter.getObjectType(), playerCharacter.getObjectUUID(), mbEnums.TransactionType.DEPOSIT, resourceType, amount);
return true;
}
public static synchronized boolean depositFromMine(Mine mine, mbEnums.ResourceType resourceType, int amount, Warehouse warehouse) {
int oldAmount = warehouse.resources.get(resourceType);
int newAmount = oldAmount + amount;
if (newAmount > resourceType.deposit_limit)
return false;
warehouse.resources.put(resourceType, newAmount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.put(resourceType, oldAmount);
return false;
}
if (mine != null)
AddTransactionToWarehouse(warehouse, mbEnums.GameObjectType.Building, mine.getBuildingID(), mbEnums.TransactionType.MINE, resourceType, amount);
return true;
}
public static synchronized void depositRealmTaxes(PlayerCharacter taxer, mbEnums.ResourceType resourceType, int amount, Warehouse warehouse) {
int oldAmount = warehouse.resources.get(resourceType);
int newAmount = oldAmount + amount;
warehouse.resources.put(resourceType, newAmount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.put(resourceType, oldAmount);
return;
}
AddTransactionToWarehouse(warehouse, taxer.getObjectType(), taxer.getObjectUUID(), mbEnums.TransactionType.TAXRESOURCEDEPOSIT, resourceType, amount);
}
public static synchronized void depositProfitTax(mbEnums.ResourceType resourceType, int amount, Building building, Warehouse warehouse) {
if (warehouse.resources.get(resourceType) == null)
return;
int oldAmount = warehouse.resources.get(resourceType);
int newAmount = oldAmount + amount;
if (newAmount > resourceType.deposit_limit)
return;
warehouse.resources.put(resourceType, newAmount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.put(resourceType, oldAmount);
return;
}
if (building != null)
AddTransactionToWarehouse(warehouse, mbEnums.GameObjectType.Building, building.getObjectUUID(), mbEnums.TransactionType.DEPOSIT, resourceType, amount);
}
public static synchronized void transferResources(Warehouse warehouse, PlayerCharacter taxer, TaxResourcesMsg msg, ArrayList<mbEnums.ResourceType> realmResources, float taxPercent) {
for (mbEnums.ResourceType resourceType : realmResources) {
if (warehouse.resources.get(resourceType) == null)
return;
int amount = (int) (warehouse.resources.get(resourceType) * taxPercent);
if (amount <= 0) {
msg.getResources().put(resourceType.resourceHash, 0);
continue;
}
int oldAmount = warehouse.resources.get(resourceType);
if (oldAmount < amount)
amount = oldAmount;
int newAmount = oldAmount - amount;
if (newAmount < amount)
continue;
msg.getResources().put(resourceType.resourceHash, amount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
msg.getResources().put(resourceType.resourceHash, 0);
warehouse.resources.put(resourceType, oldAmount);
continue;
}
warehouse.resources.put(resourceType, newAmount);
depositRealmTaxes(taxer, resourceType, amount, warehouse);
AddTransactionToWarehouse(warehouse, taxer.getObjectType(), taxer.getObjectUUID(), mbEnums.TransactionType.TAXRESOURCE, resourceType, amount);
}
}
public static synchronized boolean withdraw(Warehouse warehouse, PlayerCharacter playerCharacter, mbEnums.ResourceType resourceType, int amount, boolean addToInventory, boolean transaction) {
if (playerCharacter == null)
return false;
ItemTemplate template = ItemTemplate.templates.get(resourceType.templateID);
if (warehouse.resources.get(resourceType) == null)
return false;
if (amount <= 0)
return false;
CharacterItemManager itemMan = playerCharacter.charItemManager;
if (itemMan == null)
return false;
if (addToInventory)
if (!itemMan.hasRoomInventory(template.item_wt)) {
ChatManager.chatSystemInfo(playerCharacter, "You can not carry any more of that item.");
return false;
}
if (addToInventory && resourceType.equals(mbEnums.ResourceType.GOLD)) {
if (playerCharacter.charItemManager.getGoldInventory().getNumOfItems() + amount > MBServerStatics.PLAYER_GOLD_LIMIT)
return false;
if (playerCharacter.charItemManager.getGoldInventory().getNumOfItems() + amount < 0)
return false;
}
int oldAmount = warehouse.resources.get(resourceType);
if (oldAmount < amount)
return false;
int newAmount = oldAmount - amount;
warehouse.resources.put(resourceType, newAmount);
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.put(resourceType, oldAmount);
return false;
}
if (addToInventory) {
if (resourceType.equals(mbEnums.ResourceType.GOLD)) {
itemMan.addGoldToInventory(amount, false);
UpdateGoldMsg ugm = new UpdateGoldMsg(playerCharacter);
ugm.configure();
Dispatch dispatch = Dispatch.borrow(playerCharacter, ugm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
itemMan.updateInventory();
} else {
boolean itemWorked = false;
Item item = new Item(resourceType.templateID);
item.ownerID = playerCharacter.getObjectUUID();
item.ownerType = mbEnums.OwnerType.PlayerCharacter;
item.containerType = mbEnums.ItemContainerType.INVENTORY;
item.numberOfItems = amount;
try {
item = DbManager.ItemQueries.PERSIST(item);
itemWorked = true;
} catch (Exception e) {
Logger.error(e);
}
if (itemWorked) {
itemMan.addItemToInventory(item);
itemMan.updateInventory();
}
}
}
if (transaction)
AddTransactionToWarehouse(warehouse, playerCharacter.getObjectType(), playerCharacter.getObjectUUID(), mbEnums.TransactionType.WITHDRAWL, resourceType, amount);
return true;
}
public static synchronized boolean loot(Warehouse warehouse, PlayerCharacter playerCharacter, mbEnums.ResourceType resourceType, int amount, boolean addToInventory) {
if (playerCharacter == null)
return false;
ItemTemplate template = ItemTemplate.templates.get(resourceType.templateID);
if (template == null)
return false;
if (warehouse.resources.get(resourceType) == null)
return false;
if (amount <= 0)
return false;
CharacterItemManager itemMan = playerCharacter.charItemManager;
if (itemMan == null)
return false;
if (!itemMan.hasRoomInventory(template.item_wt)) {
ChatManager.chatSystemInfo(playerCharacter, "You can not carry any more of that item.");
return false;
}
int oldAmount = warehouse.resources.get(resourceType);
if (oldAmount < amount)
return false;
int newAmount = oldAmount - amount;
warehouse.resources.put(resourceType, newAmount);
if (addToInventory) {
if (resourceType.equals(mbEnums.ResourceType.GOLD)) {
itemMan.addGoldToInventory(amount, false);
UpdateGoldMsg ugm = new UpdateGoldMsg(playerCharacter);
ugm.configure();
Dispatch dispatch = Dispatch.borrow(playerCharacter, ugm);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
itemMan.updateInventory();
} else {
boolean itemWorked = false;
Item item = new Item(resourceType.templateID);
item.ownerID = playerCharacter.getObjectUUID();
item.ownerType = mbEnums.OwnerType.PlayerCharacter;
item.containerType = mbEnums.ItemContainerType.INVENTORY;
item.numberOfItems = amount;
try {
item = DbManager.ItemQueries.PERSIST(item);
itemWorked = true;
} catch (Exception e) {
Logger.error(e);
}
if (itemWorked) {
itemMan.addItemToInventory(item);
itemMan.updateInventory();
}
}
}
return true;
}
public static boolean isEmpty(Warehouse warehouse) {
int amount = 0;
for (mbEnums.ResourceType resourceType : EnumSet.allOf(mbEnums.ResourceType.class)) {
amount += warehouse.resources.get(resourceType);
if (amount > 0)
return false;
}
return true;
}
public static void AddTransactionToWarehouse(Warehouse warehouse, mbEnums.GameObjectType targetType, int targetUUID, mbEnums.TransactionType transactionType, mbEnums.ResourceType resource, int amount) {
if (!DbManager.WarehouseQueries.CREATE_TRANSACTION(warehouse.building.getObjectUUID(), targetType, targetUUID, transactionType, resource, amount, DateTime.now()))
return;
Transaction transaction = new Transaction(warehouse.building.getObjectUUID(), targetType, targetUUID, transactionType, resource, amount, DateTime.now());
warehouse.transactions.add(transaction);
}
public static boolean isAboveCap(Warehouse warehouse, mbEnums.ResourceType resourceType, int deposit) {
int newAmount = warehouse.resources.get(resourceType) + deposit;
return newAmount > resourceType.deposit_limit;
}
public static boolean isResourceLocked(Warehouse warehouse, mbEnums.ResourceType resourceType) {
return warehouse.locked.contains(resourceType);
}
public static HashMap<mbEnums.ResourceType, Integer> calcCostOverrun(WorkOrder workOrder) {
HashMap<mbEnums.ResourceType, Integer> costMap = new HashMap<>();
Warehouse warehouse;
// See if we can meet gold only requirements and early exit
if (workOrder.production_cost_total.size() == 1) {
if (workOrder.vendor.building.getStrongboxValue() > workOrder.production_cost_total.get(mbEnums.ResourceType.GOLD))
return costMap;
}
// Gold deficit exists so a warehouse is required
warehouse = workOrder.vendor.building.getCity() == null ? null : workOrder.vendor.building.getCity().warehouse;
if (warehouse == null)
return workOrder.production_cost_total;
// Method returns a map of resourceType that a transaction overdrafts.
HashMap<mbEnums.ResourceType, Integer> overflowMap = new HashMap<>();
for (mbEnums.ResourceType resourceType : workOrder.production_cost_total.keySet()) {
int debit = warehouse.resources.get(resourceType) - workOrder.production_cost_total.get(resourceType);
// Locked resources are always unavailable
if (debit < 0 || warehouse.locked.contains(resourceType))
overflowMap.put(resourceType, debit);
}
return overflowMap;
}
}