public void HandleSalvaging(List <uint> salvageItems) { var salvageBags = new List <WorldObject>(); var salvageResults = new SalvageResults(); foreach (var itemGuid in salvageItems) { var item = GetInventoryItem(itemGuid); if (item == null) { log.Debug($"[CRAFTING] {Name}.HandleSalvaging({itemGuid:X8}): couldn't find inventory item"); continue; } if (item.MaterialType == null) { log.Warn($"{Name}.HandleSalvaging({item.Name}): no material type"); continue; } if (IsTrading && ItemsInTradeWindow.Contains(item.Guid)) { SendWeenieError(WeenieError.YouCannotSalvageItemsInTrading); continue; } if (item.Workmanship == null || item.Retained) { continue; } AddSalvage(salvageBags, item, salvageResults); // can any salvagable items be stacked? TryConsumeFromInventoryWithNetworking(item); } // add salvage bags foreach (var salvageBag in salvageBags) { TryCreateInInventoryWithNetworking(salvageBag); } // send network messages if (!SquelchManager.Squelches.Contains(this, ChatMessageType.Salvaging)) { foreach (var kvp in salvageResults.GetMessages()) { Session.Network.EnqueueSend(new GameEventSalvageOperationsResult(Session, kvp.Key, kvp.Value)); } } }
/// <summary> /// Handles the 'GameAction 0x36 - UseItem' network message /// when player double clicks an item /// </summary> public void HandleActionUseItem(uint itemGuid) { if (PKLogout) { SendUseDoneEvent(WeenieError.YouHaveBeenInPKBattleTooRecently); return; } StopExistingMoveToChains(); var item = FindObject(itemGuid, SearchLocations.MyInventory | SearchLocations.MyEquippedItems | SearchLocations.Landblock); if (IsTrading && ItemsInTradeWindow.Contains(item.Guid)) { SendUseDoneEvent(WeenieError.TradeItemBeingTraded); //SendWeenieError(WeenieError.TradeItemBeingTraded); return; } if (item != null) { if (item.CurrentLandblock != null && !item.Visibility && item.Guid != LastOpenedContainerId) { if (IsBusy) { SendUseDoneEvent(WeenieError.YoureTooBusy); return; } CreateMoveToChain(item, (success) => TryUseItem(item, success)); } else { TryUseItem(item); } } else { log.Debug($"{Name}.HandleActionUseItem({itemGuid:X8}): couldn't find object"); SendUseDoneEvent(); } }
/// <summary> /// Handles the 'GameAction 0x35 - UseWithTarget' network message /// when player double clicks an inventory item resulting in a target indicator /// and then clicks another item /// </summary> public void HandleActionUseWithTarget(uint sourceObjectGuid, uint targetObjectGuid) { if (PKLogout) { SendUseDoneEvent(WeenieError.YouHaveBeenInPKBattleTooRecently); return; } StopExistingMoveToChains(); // source item is always in our possession var sourceItem = FindObject(sourceObjectGuid, SearchLocations.MyInventory | SearchLocations.MyEquippedItems, out _, out _, out var sourceItemIsEquipped); if (sourceItem == null) { log.Warn($"{Name}.HandleActionUseWithTarget({sourceObjectGuid:X8}, {targetObjectGuid:X8}): couldn't find {sourceObjectGuid:X8}"); SendUseDoneEvent(); return; } // handle casters with built-in spells if (sourceItemIsEquipped) { if (sourceItem.SpellDID != null) { // check activation requirements var result = sourceItem.CheckUseRequirements(this); if (!result.Success) { if (result.Message != null) { Session.Network.EnqueueSend(result.Message); } SendUseDoneEvent(); } else { HandleActionCastTargetedSpell(targetObjectGuid, sourceItem.SpellDID ?? 0, true); } } else { SendUseDoneEvent(); } return; } // Resolve the guid to an object that is either in our possession or on the Landblock var target = FindObject(targetObjectGuid, SearchLocations.MyInventory | SearchLocations.MyEquippedItems | SearchLocations.Landblock); if (target == null) { log.Warn($"{Name}.HandleActionUseWithTarget({sourceObjectGuid:X8}, {targetObjectGuid:X8}): couldn't find {targetObjectGuid:X8}"); SendUseDoneEvent(); return; } if (IsTrading) { if (ItemsInTradeWindow.Contains(sourceItem.Guid)) { SendUseDoneEvent(WeenieError.TradeItemBeingTraded); //SendWeenieError(WeenieError.TradeItemBeingTraded); return; } if (ItemsInTradeWindow.Contains(target.Guid)) { SendUseDoneEvent(WeenieError.TradeItemBeingTraded); //SendWeenieError(WeenieError.TradeItemBeingTraded); return; } } // re-verify client checks if (((sourceItem.TargetType ?? ItemType.None) & target.ItemType) == ItemType.None) { // ItemHolder::TargetCompatibleWithObject SendTransientError($"Cannot use the {sourceItem.Name} with the {target.Name}"); SendUseDoneEvent(); return; } sourceItem.HandleActionUseOnTarget(this, target); }
/// <summary> /// Client Calls this when Sell is clicked. /// </summary> public void HandleActionSellItem(List <ItemProfile> itemprofiles, uint vendorGuid) { if (IsBusy) { SendUseDoneEvent(WeenieError.YoureTooBusy); return; } var vendor = CurrentLandblock?.GetObject(vendorGuid) as Vendor; if (vendor == null) { SendUseDoneEvent(WeenieError.NoObject); return; } itemprofiles = VerifySellItems(itemprofiles, vendor); var allPossessions = GetAllPossessions(); var sellList = new List <WorldObject>(); var acceptedItemTypes = (ItemType)(vendor.MerchandiseItemTypes ?? 0); foreach (ItemProfile profile in itemprofiles) { var item = allPossessions.FirstOrDefault(i => i.Guid.Full == profile.ObjectGuid); if (item == null) { continue; } if ((acceptedItemTypes & item.ItemType) == 0 || !item.IsSellable || item.Retained) { var itemName = (item.StackSize ?? 1) > 1 ? item.GetPluralName() : item.Name; Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, $"The {itemName} is unsellable.")); // retail message did not include item name, leaving in that for now. continue; } if (item.Value < 1) { var itemName = (item.StackSize ?? 1) > 1 ? item.GetPluralName() : item.Name; Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, $"The {itemName} has no value and cannot be sold.")); // retail message did not include item name, leaving in that for now. continue; } if (IsTrading && ItemsInTradeWindow.Contains(item.Guid)) { var itemName = (item.StackSize ?? 1) > 1 ? item.GetPluralName() : item.Name; Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, $"You cannot sell that! The {itemName} is currently being traded.")); // custom message? continue; } sellList.Add(item); } if (sellList.Count == 0) { Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); SendUseDoneEvent(WeenieError.None); return; } var payoutCoinAmount = vendor.CalculatePayoutCoinAmount(sellList); if (payoutCoinAmount < 0) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "Transaction failed.")); log.Warn($"{Name} (0x({Guid}) tried to sell something to {vendor.Name} (0x{vendor.Guid}) resulting in a payout of {payoutCoinAmount} pyreals."); Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); SendUseDoneEvent(); return; } var playerFreeInventorySlots = GetFreeInventorySlots(); var playerAvailableBurden = GetAvailableBurden(); var numberOfCoinStacksToCreate = PreCheckItem(coinStackWeenieClassId, payoutCoinAmount, 0, GetFreeInventorySlots(), GetAvailableBurden(), out var totalEncumburanceOfCoinStacks, out _); var playerDoesNotHaveEnoughPackSpace = playerFreeInventorySlots < numberOfCoinStacksToCreate; var playerDoesNotHaveEnoughBurdenCapacity = playerAvailableBurden < totalEncumburanceOfCoinStacks; if (playerDoesNotHaveEnoughPackSpace || playerDoesNotHaveEnoughBurdenCapacity) { if (playerDoesNotHaveEnoughBurdenCapacity) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "You are too encumbered to sell that!")); } else // if (playerDoesNotHaveEnoughPackSpace) { 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(); return; } var payoutCoinStacks = CreatePayoutCoinStacks(payoutCoinAmount); // Make sure we have enough pack space for the payout if (GetFreeInventorySlots() + sellList.Count - payoutCoinStacks.Count < 0) { Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "Not enough inventory space!")); // TODO: find retail messages Session.Network.EnqueueSend(new GameEventInventoryServerSaveFailed(Session, Guid.Full)); foreach (var item in payoutCoinStacks) { item.Destroy(); } SendUseDoneEvent(WeenieError.FullInventoryLocation); return; } // Remove the items we're selling from our inventory foreach (var item in sellList) { if (TryRemoveFromInventoryWithNetworking(item.Guid, out _, RemoveFromInventoryAction.SellItem) || TryDequipObjectWithNetworking(item.Guid, out _, DequipObjectAction.SellItem)) { Session.Network.EnqueueSend(new GameEventItemServerSaysContainId(Session, item, vendor)); } else { log.WarnFormat("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 to complete the transaction vendor.ProcessItemsForPurchase(this, sellList); // Add the payout to inventory foreach (var item in payoutCoinStacks) { if (!TryCreateInInventoryWithNetworking(item)) // This shouldn't happen { log.WarnFormat("Payout 0x{0:X8}:{1} for player {2} failed to add to inventory HandleActionSellItem.", item.Guid.Full, item.Name, Name); item.Destroy(); } } UpdateCoinValue(false); Session.Network.EnqueueSend(new GameMessageSound(Guid, Sound.PickUpItem)); SendUseDoneEvent(); }
/// <summary> /// Handles the 'GameAction 0x35 - UseWithTarget' network message /// when player double clicks an inventory item resulting in a target indicator /// and then clicks another item /// </summary> public void HandleActionUseWithTarget(uint sourceObjectGuid, uint targetObjectGuid) { if (PKLogout) { SendUseDoneEvent(WeenieError.YouHaveBeenInPKBattleTooRecently); return; } StopExistingMoveToChains(); // source item is always in our possession var sourceItem = FindObject(sourceObjectGuid, SearchLocations.MyInventory | SearchLocations.MyEquippedItems, out _, out _, out var sourceItemIsEquipped); if (sourceItem == null) { log.Warn($"{Name}.HandleActionUseWithTarget({sourceObjectGuid:X8}, {targetObjectGuid:X8}): couldn't find {sourceObjectGuid:X8}"); SendUseDoneEvent(); return; } // handle casters with built-in spells if (sourceItemIsEquipped) { if (sourceItem.SpellDID != null) { // check activation requirements var result = sourceItem.CheckUseRequirements(this); if (!result.Success) { if (result.Message != null) { Session.Network.EnqueueSend(result.Message); } SendUseDoneEvent(); } else { HandleActionCastTargetedSpell(targetObjectGuid, sourceItem.SpellDID ?? 0, true); } } else { SendUseDoneEvent(); } return; } // Resolve the guid to an object that is either in our possession or on the Landblock var target = FindObject(targetObjectGuid, SearchLocations.MyInventory | SearchLocations.MyEquippedItems | SearchLocations.Landblock); if (target == null) { log.Warn($"{Name}.HandleActionUseWithTarget({sourceObjectGuid:X8}, {targetObjectGuid:X8}): couldn't find {targetObjectGuid:X8}"); SendUseDoneEvent(); return; } if (IsTrading) { if (ItemsInTradeWindow.Contains(sourceItem.Guid)) { SendUseDoneEvent(WeenieError.TradeItemBeingTraded); //SendWeenieError(WeenieError.TradeItemBeingTraded); return; } if (ItemsInTradeWindow.Contains(target.Guid)) { SendUseDoneEvent(WeenieError.TradeItemBeingTraded); //SendWeenieError(WeenieError.TradeItemBeingTraded); return; } } // re-verify client checks if (((sourceItem.TargetType ?? ItemType.None) & target.ItemType) == ItemType.None) { // ItemHolder::TargetCompatibleWithObject SendTransientError($"Cannot use the {sourceItem.Name} with the {target.Name}"); SendUseDoneEvent(); return; } if (target.CurrentLandblock != null && target != this) { // todo: verify target can be used remotely // move RecipeManager.VerifyUse logic into base Player_Use // this was avoided because i didn't want to deal with the ramifications of random items missing the correct ItemUseable flags, // and because there are still some ItemUseable flags with missing logic we haven't quite figured out yet if (IsBusy) { SendUseDoneEvent(WeenieError.YoureTooBusy); return; } CreateMoveToChain(target, (success) => { if (success) { sourceItem.HandleActionUseOnTarget(this, target); } else { SendUseDoneEvent(); } }); } else { sourceItem.HandleActionUseOnTarget(this, target); } }