public bool HandleUseCreateItem(Player player) { var amount = UseCreateQuantity ?? 1; var itemsToReceive = new ItemsToReceive(player); itemsToReceive.Add(UseCreateItem.Value, amount); if (itemsToReceive.PlayerExceedsLimits) { if (itemsToReceive.PlayerExceedsAvailableBurden) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, "You are too encumbered to use that!")); } else if (itemsToReceive.PlayerOutOfInventorySlots) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, "You do not have enough pack space to use that!")); } else if (itemsToReceive.PlayerOutOfContainerSlots) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, "You do not have enough container slots to use that!")); } return(false); } if (itemsToReceive.RequiredSlots > 0) { var remaining = amount; while (remaining > 0) { var item = WorldObjectFactory.CreateNewWorldObject(UseCreateItem.Value); if (item is Stackable) { var stackSize = Math.Min(remaining, item.MaxStackSize ?? 1); item.SetStackSize(stackSize); remaining -= stackSize; } else { remaining--; } player.TryCreateInInventoryWithNetworking(item); } } else { player.SendTransientError($"Unable to use {Name} at this time!"); return(false); } return(true); }
// player selling items to vendor // whereas most of the logic for buying items is in vendor, // most of the logic for selling items is located in player_commerce // the functions have similar structure, just in different places // there's really no point in there being differences in location, // and it might be better to move them all to vendor for consistency. /// <summary> /// Called when player clicks 'Sell Items' /// </summary> public void HandleActionSellItem(uint vendorGuid, List <ItemProfile> itemProfiles) { if (IsBusy) { SendUseDoneEvent(WeenieError.YoureTooBusy); return; } var vendor = CurrentLandblock?.GetObject(vendorGuid) as Vendor; if (vendor == null) { SendUseDoneEvent(WeenieError.NoObject); return; } // perform validations on requested sell items, // and filter to list of validated items // one difference between sell and buy is here. // when an itemProfile is invalid in buy, the entire transaction is failed immediately. // when an itemProfile is invalid in sell, we just remove the invalid itemProfiles, and continue onwards // this might not be the best for safety, and it's a tradeoff between safety and player convenience // should we fail the entire transaction (similar to buy), if there are any invalids in the transaction request? var sellList = VerifySellItems(itemProfiles, vendor); if (sellList.Count == 0) { Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); SendUseDoneEvent(); return; } // calculate pyreals to receive var payoutCoinAmount = vendor.CalculatePayoutCoinAmount(sellList); if (payoutCoinAmount < 0) { log.Warn($"[VENDOR] {Name} (0x({Guid}) tried to sell something to {vendor.Name} (0x{vendor.Guid}) resulting in a payout of {payoutCoinAmount} pyreals."); SendTransientError("Transaction failed."); Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); SendUseDoneEvent(); return; } // verify player has enough pack slots / burden to receive these pyreals var itemsToReceive = new ItemsToReceive(this); itemsToReceive.Add((uint)ACE.Entity.Enum.WeenieClassName.W_COINSTACK_CLASS, payoutCoinAmount); if (itemsToReceive.PlayerExceedsLimits) { if (itemsToReceive.PlayerExceedsAvailableBurden) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "You are too encumbered to sell that!")); } else if (itemsToReceive.PlayerOutOfInventorySlots) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "You do not have enough free pack space to sell that!")); } Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); SendUseDoneEvent(); // WeenieError.FullInventoryLocation? return; } var payoutCoinStacks = CreatePayoutCoinStacks(payoutCoinAmount); vendor.MoneyOutflow += payoutCoinAmount; // remove sell items from player inventory foreach (var item in sellList.Values) { if (TryRemoveFromInventoryWithNetworking(item.Guid, out _, RemoveFromInventoryAction.SellItem) || TryDequipObjectWithNetworking(item.Guid, out _, DequipObjectAction.SellItem)) { Session.Network.EnqueueSend(new GameEventItemServerSaysContainId(Session, item, vendor)); } else { log.WarnFormat("[VENDOR] Item 0x{0:X8}:{1} for player {2} not found in HandleActionSellItem.", item.Guid.Full, item.Name, Name); // This shouldn't happen } } // send the list of items to the vendor // for the vendor to determine what to do with each item (resell, destroy) vendor.ProcessItemsForPurchase(this, sellList); // add coins to player inventory foreach (var item in payoutCoinStacks) { if (!TryCreateInInventoryWithNetworking(item)) // this shouldn't happen because of pre-validations in itemsToReceive { log.WarnFormat("[VENDOR] Payout 0x{0:X8}:{1} for player {2} failed to add to inventory HandleActionSellItem.", item.Guid.Full, item.Name, Name); item.Destroy(); } } // UpdateCoinValue removed -- already handled in TryCreateInInventoryWithNetworking Session.Network.EnqueueSend(new GameMessageSound(Guid, Sound.PickUpItem)); SendUseDoneEvent(); }