private static bool SharedTryFindItemsOfType(
            IItemsContainerProvider containers,
            IProtoItem requiredProtoItem,
            uint count,
            out List <IItem> result)
        {
            var countToFindRemains = (int)count;

            result = new List <IItem>();
            foreach (var container in containers.ItemsContainers)
            {
                foreach (var item in container.Items)
                {
                    if (item.ProtoItem != requiredProtoItem)
                    {
                        continue;
                    }

                    result.Add(item);
                    countToFindRemains -= item.Count;
                    if (countToFindRemains <= 0)
                    {
                        break;
                    }
                }

                if (countToFindRemains <= 0)
                {
                    break;
                }
            }

            return(countToFindRemains <= 0);
        }
Exemple #2
0
        private static void ServerDestroyInputItems(
            Recipe recipe,
            IItemsContainerProvider inputContainers,
            ushort countToCraft,
            bool isCreativeMode)
        {
            foreach (var inputItem in recipe.InputItems)
            {
                var countToDestroy = (ushort)(inputItem.Count * countToCraft);
                var inputItemProto = inputItem.ProtoItem;

                if (isCreativeMode)
                {
                    CreativeModeAdjustCountToDestroy(inputItemProto, ref countToDestroy);
                    if (countToDestroy == 0)
                    {
                        continue;
                    }
                }

                ServerDestroyItemsOfType(inputContainers,
                                         inputItemProto,
                                         countToDestroy,
                                         out _);
            }

            void CreativeModeAdjustCountToDestroy(IProtoItem inputItemProto, ref ushort countToDestroy)
            {
                // destroy only available count
                var availableCount = 0;

                foreach (var c in inputContainers.Containers)
                {
                    foreach (var item in c.Items)
                    {
                        if (ReferenceEquals(item.ProtoItem, inputItemProto))
                        {
                            availableCount += item.Count;
                        }
                    }
                }

                if (availableCount == 0)
                {
                    // nothing to destroy
                    countToDestroy = 0;
                    return;
                }

                if (countToDestroy > availableCount)
                {
                    // limit count to destroy
                    countToDestroy = (ushort)availableCount;
                }
            }
        }
Exemple #3
0
        private static bool SharedTryFindItemsOfType(
            IItemsContainerProvider containers,
            IProtoItem requiredProtoItem,
            uint count,
            out List <IItem> result,
            double minQualityFraction)
        {
            var countToFindRemains = (int)count;

            result = new List <IItem>();
            foreach (var container in containers.ItemsContainers)
            {
                foreach (var item in container.Items)
                {
                    if (item.ProtoItem != requiredProtoItem)
                    {
                        continue;
                    }

                    if (requiredProtoItem is IProtoItemWithDurability &&
                        ItemDurabilitySystem.SharedGetDurabilityFraction(item) < minQualityFraction)
                    {
                        // not enough durability
                        continue;
                    }

                    if (requiredProtoItem is IProtoItemWithFreshness &&
                        ItemFreshnessSystem.SharedGetFreshnessFraction(item) < minQualityFraction)
                    {
                        // not enough durability
                        continue;
                    }

                    result.Add(item);
                    countToFindRemains -= item.Count;
                    if (countToFindRemains <= 0)
                    {
                        break;
                    }
                }

                if (countToFindRemains <= 0)
                {
                    break;
                }
            }

            return(countToFindRemains <= 0);
        }
Exemple #4
0
        private static void ServerDestroyInputItems(
            Recipe recipe,
            IItemsContainerProvider inputContainers,
            ushort countToCraft,
            bool isAdminMode)
        {
            var itemsService = Api.Server.Items;

            foreach (var inputItem in recipe.InputItems)
            {
                var countToDestroy = (ushort)(inputItem.Count * countToCraft);
                var inputItemType  = inputItem.ProtoItem;

                if (isAdminMode)
                {
                    // destroy only available count
                    var availableCount =
                        inputContainers.Sum(c => c.Items.Where(i => i.ProtoItem == inputItemType).Sum(i => i.Count));
                    if (availableCount == 0)
                    {
                        // nothing to destroy
                        continue;
                    }

                    if (countToDestroy > availableCount)
                    {
                        // limit count to destroy
                        countToDestroy = (ushort)availableCount;
                    }
                }

                itemsService.DestroyItemsOfType(
                    inputContainers,
                    inputItemType,
                    countToDestroy,
                    out _);
            }
        }
        private static void ServerDestroyInputItems(
            Recipe recipe,
            IItemsContainerProvider inputContainers,
            ushort countToCraft,
            bool isCreativeMode)
        {
            foreach (var inputItem in recipe.InputItems)
            {
                var countToDestroy = (ushort)(inputItem.Count * countToCraft);
                var inputItemProto = inputItem.ProtoItem;

                if (isCreativeMode)
                {
                    // destroy only available count
                    var availableCount = inputContainers.Sum(
                        c => c.Items.Where(i => ReferenceEquals(i.ProtoItem, inputItemProto))
                        .Sum(i => i.Count));
                    if (availableCount == 0)
                    {
                        // nothing to destroy
                        continue;
                    }

                    if (countToDestroy > availableCount)
                    {
                        // limit count to destroy
                        countToDestroy = (ushort)availableCount;
                    }
                }

                ServerDestroyItemsOfType(
                    inputContainers,
                    inputItemProto,
                    countToDestroy,
                    out _);
            }
        }
Exemple #6
0
        private static void ServerDestroyItemsOfType(
            IItemsContainerProvider containers,
            IProtoItem protoItem,
            uint countToDestroy,
            out uint destroyedCount)
        {
            var serverItemsService = Api.Server.Items;

            if (protoItem is not IProtoItemWithFreshness)
            {
                // for items without freshness use the default approach
                serverItemsService.DestroyItemsOfType(containers,
                                                      protoItem,
                                                      countToDestroy,
                                                      out destroyedCount);
                return;
            }

            // for items with freshness, gather all the available items and then sort them by freshness
            // prefer items with lower freshness first
            destroyedCount = 0;

            using var tempAllItemsOfType = Api.Shared.GetTempList <IItem>();
            var allItemsOfType = tempAllItemsOfType.AsList();

            foreach (var container in containers.Containers)
            {
                foreach (var item in container.Items)
                {
                    if (ReferenceEquals(item.ProtoItem, protoItem))
                    {
                        allItemsOfType.Add(item);
                    }
                }
            }

            SortItems(protoItem, allItemsOfType);

            // loop from the slot with lowest freshness to the slow with the higher freshness
            // (if the freshness it the same, it should go right-to-left unless we got a sorting issue)
            for (var index = allItemsOfType.Count - 1; index >= 0; index--)
            {
                var item = allItemsOfType[index];
                if (item.Count >= countToDestroy)
                {
                    // remove full remained count
                    destroyedCount += countToDestroy;
                    serverItemsService.SetCount(item, (ushort)(item.Count - countToDestroy));
                    countToDestroy = 0;
                    return;
                }

                // remove as much as we can
                destroyedCount += item.Count;
                countToDestroy -= item.Count;
                serverItemsService.DestroyItem(item);
            }

            if (destroyedCount != countToDestroy)
            {
                Api.Logger.Error(
                    $"Cannot remove all required to remove count. Containers {containers.Containers.GetJoinedString()}, item type {protoItem}, count destroyed {destroyedCount}, count to destroy remains {countToDestroy - destroyedCount}");
            }
        private static TradingResult ServerExecuteTrade(
            TradingStationLot lot,
            IItemsContainerProvider sellerContainers,
            IItemsContainerProvider buyerContainers,
            bool isPlayerBuying)
        {
            // find items to buy by other party
            if (!SharedTryFindItemsOfType(sellerContainers, lot.ProtoItem, lot.LotQuantity, out var itemsToSell))
            {
                return(isPlayerBuying
                           ? TradingResult.ErrorNotEnoughItemsOnStation
                           : TradingResult.ErrorNotEnoughItemsOnPlayer);
            }

            // try to find money to pay to other party
            var countCoinPenny = (uint)lot.PriceCoinPenny;
            var countCoinShiny = (uint)lot.PriceCoinShiny;

            if (!SharedTryFindItemsOfType(buyerContainers,
                                          ProtoItemCoinPenny.Value,
                                          countCoinPenny,
                                          out _) ||
                !SharedTryFindItemsOfType(buyerContainers,
                                          ProtoItemCoinShiny.Value,
                                          countCoinShiny,
                                          out _))
            {
                return(isPlayerBuying
                           ? TradingResult.ErrorNotEnoughMoneyOnStation
                           : TradingResult.ErrorNotEnoughMoneyOnPlayer);
            }

            // ensure there is enough space to store the sold items
            if (!ServerItems.CanCreateItem(buyerContainers, lot.ProtoItem, lot.LotQuantity))
            {
                return(isPlayerBuying
                           ? TradingResult.ErrorNotEnoughSpaceOnPlayerForPurchasedItem
                           : TradingResult.ErrorNotEnoughSpaceOnStationForSoldItem);
            }

            // try create money in the source containers
            var sourceContainerResult = new CreateItemResult()
            {
                IsEverythingCreated = true
            };

            if (lot.PriceCoinPenny > 0)
            {
                sourceContainerResult.MergeWith(
                    ServerItems.CreateItem(ProtoItemCoinPenny.Value,
                                           sellerContainers,
                                           countCoinPenny));
            }

            if (lot.PriceCoinShiny > 0)
            {
                sourceContainerResult.MergeWith(
                    ServerItems.CreateItem(ProtoItemCoinShiny.Value,
                                           sellerContainers,
                                           countCoinShiny));
            }

            if (!sourceContainerResult.IsEverythingCreated)
            {
                sourceContainerResult.Rollback();
                // TODO: check this
                return(isPlayerBuying
                           ? TradingResult.ErrorNotEnoughSpaceOnStationForSoldItem
                           : TradingResult.ErrorNotEnoughSpaceOnPlayerForPurchasedItem);
            }

            // try moving (bought) items
            var itemsCountToDestroyRemains = (int)lot.LotQuantity;

            foreach (var item in itemsToSell)
            {
                if (itemsCountToDestroyRemains <= 0)
                {
                    break;
                }

                ServerItems.MoveOrSwapItem(item,
                                           buyerContainers,
                                           out var movedCount,
                                           countToMove: (ushort)itemsCountToDestroyRemains);
                itemsCountToDestroyRemains -= movedCount;
            }

            if (itemsCountToDestroyRemains > 0)
            {
                // should be impossible
                Logger.Error(
                    "Cannot move all sold items! But we've verified that the sellerContainers have them all...");
            }

            if (countCoinPenny > 0)
            {
                ServerItems.DestroyItemsOfType(buyerContainers, ProtoItemCoinPenny.Value, countCoinPenny, out _);
            }

            if (countCoinShiny > 0)
            {
                ServerItems.DestroyItemsOfType(buyerContainers, ProtoItemCoinShiny.Value, countCoinShiny, out _);
            }

            Logger.Important($"Successfully completed trading transaction: {lot}");
            return(TradingResult.Success);
        }