// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.gameManager; import engine.loot.WorkOrder; import engine.mbEnums; import engine.objects.Item; import engine.objects.ItemTemplate; import engine.objects.MobLoot; import engine.objects.Warehouse; import engine.powers.EffectsBase; import org.pmw.tinylog.Logger; import java.util.HashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.DelayQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public enum ForgeManager implements Runnable { FORGE_MANAGER; private static final BlockingQueue forge = new DelayQueue<>(); public static final AtomicInteger wordOrderCounter = new AtomicInteger(0); public static final HashMap workOrderMap = new HashMap(); @Override public void run() { while (true) { try { WorkOrder workOrder = forge.take(); // Completed or canceled work orders are not re-enqueued if (workOrder.runCanceled || workOrder.runCompleted) continue; // Create in memory items to add to collections forgeItems(workOrder); Logger.info("item forged:" + workOrder.workOrderID + " (" + workOrder.total_produced + "/" + workOrder.total_to_produce + ")"); if (workOrder.total_produced >= workOrder.total_to_produce) { Logger.info("workOrder has completed: " + workOrder.workOrderID); // Persist current items that are currently building for this // worker after first removing the negative id item from all // collections. // Add new item to the vendors inventory workOrder.runCompleted = true; workOrder.vendor.workOrders.remove(workOrder); continue; } // enQueue this workOrder again; back into the oven // until all items for this workOrder are completed. workOrder.completionTime = System.currentTimeMillis() + workOrder.rollingDuration; forge.add(workOrder); Logger.info(workOrder.toString()); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void start() { Thread forgeManager; forgeManager = new Thread(FORGE_MANAGER); forgeManager.setName("Forge Manager"); forgeManager.start(); } public static int submit(WorkOrder workOrder) { int validation_result = ForgeManager.validate(workOrder); if (validation_result != 0) return validation_result; workOrder.workOrderID = wordOrderCounter.incrementAndGet(); workOrder.rollingDuration = ForgeManager.calcRollingDuration(workOrder); workOrder.completionTime = System.currentTimeMillis() + workOrder.rollingDuration; workOrder.slots_used = calcAvailableSlots(workOrder); // Cost to execute this workOrder workOrder.production_cost = calcProductionCost(workOrder); // Set total cost for this production run workOrder.production_cost_total.putAll(workOrder.production_cost); workOrder.production_cost_total.forEach((key, value) -> workOrder.production_cost_total.compute(key, (k, v) -> v * workOrder.total_to_produce)); workOrder.total_to_produce *= workOrder.slots_used; // Create in-memory items and add to collections forgeItems(workOrder); Logger.info(workOrder.toString()); workOrder.vendor.workOrders.add(workOrder); forge.add(workOrder); return validation_result; } public static int validate(WorkOrder workOrder) { int validation_result = 0; ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); if (!workOrder.vendor.charItemManager.hasRoomInventory(template.item_wt)) return 30; //30: That person cannot carry that item if (!workOrder.vendor.getItemModTable().contains(((byte) template.modTable))) return 59; //59: This hireling does not have this formula if (!calcCostOverrun(workOrder).isEmpty()) return 10; //18: You can't really afford that // Forge must be protected in order to access warehouse. if (calcProductionCost(workOrder).size() > 1) if (!workOrder.vendor.building.protectionState.equals(mbEnums.ProtectionState.PROTECTED)) return 193; //193: Production denied: This building must be protected to gain access to warehouse return validation_result; } public static long calcRollingDuration(WorkOrder workOrder) { float rollingDuration; rollingDuration = workOrder.vendor.getBuilding().getRank() * -5L + 40; rollingDuration = TimeUnit.MINUTES.toMillis((long) rollingDuration); rollingDuration *= Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()); ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); // Bane circles if (template.item_bane_rank > 0) rollingDuration = (long) template.item_bane_rank * 60 * 60 * 3 * 1000 * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()); return (long) rollingDuration; } public static int calcAvailableSlots(WorkOrder workOrder) { // Slots available in a forge are based on the npc rank int availableSlots = workOrder.vendor.getRank(); // Slots currently used up by the npc workOrders for (WorkOrder npcWorkOrder : workOrder.vendor.workOrders) availableSlots = availableSlots - npcWorkOrder.slots_used; // Single item rolls are msg_size of 0; if (availableSlots > 0 && workOrder.multiple_slot_request == 0) availableSlots = 1; return availableSlots; } public static HashMap calcProductionCost(WorkOrder workOrder) { // Calculate the production cost for a single run of this workOrder HashMap production_cost = new HashMap<>(); ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID); // Add gold and resource costs from template production_cost.put(mbEnums.ResourceType.GOLD, template.item_value); production_cost.putAll(template.item_resource_cost); // Calculate cost of prefix and suffix if (workOrder.prefixToken != 0) { EffectsBase prefix = PowersManager.getEffectByToken(workOrder.prefixToken); EffectsBase prefixValue = PowersManager.getEffectByIDString(prefix.getIDString() + 'A'); production_cost.putAll(prefixValue.getResourcesForEffect()); } if (workOrder.suffixToken != 0) { EffectsBase suffix = PowersManager.getEffectByToken(workOrder.suffixToken); EffectsBase suffixValue = PowersManager.getEffectByIDString(suffix.getIDString() + 'A'); production_cost.putAll(suffixValue.getResourcesForEffect()); } return production_cost; } public static HashMap calcCostOverrun(WorkOrder workOrder) { HashMap 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 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; } public static Item forgeItem(WorkOrder workOrder) { Item forgedItem = new Item(workOrder.templateID); forgedItem.objectUUID = MobLoot.lastNegativeID.getAndDecrement(); forgedItem.containerType = mbEnums.ItemContainerType.FORGE; forgedItem.ownerID = workOrder.vendor.getObjectUUID(); forgedItem.setDateToUpgrade(workOrder.completionTime); // Forged random rolled items are unidentified until completed if (workOrder.prefixToken == 0 && workOrder.suffixToken == 0) forgedItem.flags.remove(mbEnums.ItemFlags.Identified); return forgedItem; } public static void forgeItems(WorkOrder workOrder) { for (int i = 0; i < workOrder.slots_used; ++i) { Item forged_item = forgeItem(workOrder); ForgeManager.workOrderMap.put(forged_item, workOrder); workOrder.cooking.add(forged_item); workOrder.total_produced = workOrder.total_produced + 1; Logger.info("Forging item: " + forged_item.objectUUID + " (" + forged_item.templateID + ") " + forged_item.template.item_base_name); } } }