/// <summary>
        /// Matches best recipe for the current input items.
        /// Please note it will return null if there best recipe is already selected.
        /// Please note it will not return better recipe if currently
        /// there is a selected recipe which using more input slots.
        /// </summary>
        public static Recipe SharedMatchBestRecipe(
            ManufacturingState state,
            ManufacturingConfig config,
            IStaticWorldObject objectManufacturer)
        {
            var selectedRecipe = state.SelectedRecipe;
            var bestRecipe     = config.MatchRecipe(objectManufacturer, state.CraftingQueue);

            if (selectedRecipe == bestRecipe)
            {
                return(null);
            }

            if (bestRecipe != null &&
                selectedRecipe != null &&
                selectedRecipe.CanBeCrafted(objectManufacturer, state.CraftingQueue, 1) &&
                bestRecipe.InputItems.Count <= selectedRecipe.InputItems.Count)
            {
                // the best recipe is ignored because selected recipe is also valid
                // and it has the same or more amount of input items
                return(null);
            }

            return(bestRecipe);
        }
        /// <summary>
        /// Updates only current recipe - useful if you need to check if there any active recipe before doing other logic.
        /// </summary>
        /// <param name="objectManufacturer">Instance of world object performing manufacturing.</param>
        /// <param name="state">Instance of manufacturing state.</param>
        /// <param name="config">Manufacturing config.</param>
        public static void UpdateRecipeOnly(
            IStaticWorldObject objectManufacturer,
            ManufacturingState state,
            ManufacturingConfig config,
            bool force = false)
        {
            var containerInputStateHash  = state.ContainerInput.StateHash;
            var containerOutputStateHash = state.ContainerOutput.StateHash;

            var containerInputChanged  = state.ContainerInputLastStateHash != containerInputStateHash;
            var containerOutputChanged = state.ContainerOutputLastStateHash != containerOutputStateHash;

            if (!containerInputChanged &&
                !containerOutputChanged &&
                !force)
            {
                return;
            }

            if (containerOutputChanged)
            {
                // ensure it will be refreshed
                state.CraftingQueue.IsContainerOutputFull = false;
            }

            state.ContainerInputLastStateHash  = containerInputStateHash;
            state.ContainerOutputLastStateHash = containerOutputStateHash;

            RefreshRecipe(state, config, objectManufacturer);
        }
        private static void OnFuelChanged(
            IStaticWorldObject objectManufacturer,
            FuelBurningState state,
            CraftingQueue byproductsCraftQueue,
            ManufacturingConfig config)
        {
            if (!config.IsProduceByproducts)
            {
                return;
            }

            var currentByproductRecipe = byproductsCraftQueue.QueueItems.FirstOrDefault()?.Recipe
                                         as Recipe.RecipeForManufacturingByproduct;
            var newByproductRecipe = config.MatchRecipeForByproduct(state.CurrentFuelItemType);

            if (currentByproductRecipe == newByproductRecipe)
            {
                return;
            }

            byproductsCraftQueue.Clear();

            if (newByproductRecipe != null)
            {
                CraftingMechanics.ServerStartCrafting(
                    objectManufacturer,
                    null,
                    byproductsCraftQueue,
                    newByproductRecipe,
                    // unlimited count
                    countToCraft: ushort.MaxValue);
            }
        }
Example #4
0
        /// <summary>
        /// Consumes fuel and populate remaining fuel burning time when needed.
        /// </summary>
        private static void RefreshFuel(
            FuelBurningState state,
            CraftingQueue byproductsCraftQueue,
            ManufacturingConfig config,
            IStaticWorldObject objectManufacturer,
            bool isNeedFuelNow)
        {
            if (state.FuelUseTimeRemainsSeconds > 0 ||
                !isNeedFuelNow ||
                state.ContainerFuel.OccupiedSlotsCount == 0)
            {
                return;
            }

            // look for the fuel item with the lowest fuel value
            IItem fuelItem            = null;
            var   lowestFuelItemValue = double.MaxValue;

            foreach (var item in state.ContainerFuel.Items)
            {
                if (item.ProtoItem is IProtoItemFuelSolid fuelItemType)
                {
                    var itemFuelAmount = fuelItemType.FuelAmount;
                    if (itemFuelAmount < lowestFuelItemValue)
                    {
                        fuelItem            = item;
                        lowestFuelItemValue = itemFuelAmount;
                    }
                }
            }

            var bestProtoFuel = fuelItem?.ProtoGameObject as IProtoItemFuelSolid;

            if (bestProtoFuel is null)
            {
                // no fuel placed in fuel container
                //if (state.CurrentFuelItemType is not null)
                //{
                //    state.CurrentFuelItemType = null;
                //    Logger.Info($"Fuel depleted for manufacturing at {objectManufacturer}");
                //}

                return;
            }

            Logger.Info($"Fuel will be used for manufacturing {bestProtoFuel.ShortId} at {objectManufacturer}");

            // destroy one fuel item
            Api.Server.Items.SetCount(fuelItem, fuelItem.Count - 1);

            // set fuel burn time
            state.CurrentFuelItemType = bestProtoFuel;
            var secondsToBurn = bestProtoFuel.FuelAmount;

            state.FuelUseTimeRemainsSeconds = secondsToBurn;

            OnFuelChanged(objectManufacturer, state, byproductsCraftQueue, config);
        }
 /// <summary>
 /// Performs full update of manufacturing state - equivalent to calling <see cref="UpdateRecipeOnly" />
 /// and then <see cref="UpdateCraftingQueueOnly" />.
 /// </summary>
 /// <param name="objectManufacturer">Instance of world object performing manufacturing.</param>
 /// <param name="state">Instance of manufacturing state.</param>
 /// <param name="config">Manufacturing config.</param>
 /// <param name="deltaTime">Delta time to progress on.</param>
 public static void Update(
     IStaticWorldObject objectManufacturer,
     ManufacturingState state,
     ManufacturingConfig config,
     double deltaTime)
 {
     UpdateRecipeOnly(objectManufacturer, state, config);
     UpdateCraftingQueueOnly(state, deltaTime);
 }
Example #6
0
        /// <summary>
        /// Refreshes best matching recipe, auto-select it if needed by config.
        /// </summary>
        private static void RefreshRecipe(
            ManufacturingState state,
            ManufacturingConfig config,
            IStaticWorldObject objectManufacturer)
        {
            var selectedRecipe = state.SelectedRecipe;
            var bestRecipe     = SharedMatchBestRecipe(state,
                                                       config,
                                                       objectManufacturer);

            if (bestRecipe is not null &&
                config.IsAutoSelectRecipe)
            {
                // auto-select the best recipe
                selectedRecipe = state.SelectedRecipe = bestRecipe;
                bestRecipe     = null;
                state.CraftingQueue.Clear();
            }

            if (selectedRecipe is null)
            {
                return;
            }

            // refresh selected recipe
            var isSelectedRecipeCanBeCrafted = selectedRecipe.CanBeCrafted(
                character: null,
                objectManufacturer,
                state.CraftingQueue,
                countToCraft: 1);

            if (isSelectedRecipeCanBeCrafted)
            {
                var currentCraftingRecipe = state.CraftingQueue.QueueItems.FirstOrDefault();
                if (currentCraftingRecipe is null ||
                    currentCraftingRecipe.RecipeEntry?.Recipe != selectedRecipe)
                {
                    // there is nothing crafting or something different is crafting - start crafting the new selected recipe
                    Logger.Info($"Manufacturing of recipe {selectedRecipe} started at {objectManufacturer}");
                    CraftingMechanics.ServerStartCrafting(
                        objectManufacturer,
                        null,
                        state.CraftingQueue,
                        new RecipeWithSkin(selectedRecipe),
                        countToCraft: ushort.MaxValue);
                }
            }
            else if (state.CraftingQueue.QueueItems.Count > 0)
            {
                // the selected recipe cannot be crafted
                // clear current queue (progress is lost!)
                // nothing will be crafted now
                Logger.Info($"Manufacturing stopped at {objectManufacturer} - the recipe cannot be crafted anymore");
                state.CraftingQueue.Clear();
            }
        }
Example #7
0
        /// <summary>
        /// Updates fuel burning state.
        /// </summary>
        /// <param name="objectManufacturer">Instance of world object performing manufacturing.</param>
        /// <param name="state">Instance of fuel burning state.</param>
        /// <param name="byproductsCraftQueue"></param>
        /// <param name="config">Manufacturing config.</param>
        /// <param name="deltaTime">Delta time to progress on.</param>
        /// <param name="isNeedFuelNow">The new fuel item will be not burned if the fuel is not needed now.</param>
        public static void Update(
            IStaticWorldObject objectManufacturer,
            FuelBurningState state,
            CraftingQueue byproductsCraftQueue,
            ManufacturingConfig config,
            double deltaTime,
            double byproductsQueueRate,
            bool isNeedFuelNow,
            bool forceRefreshFuel = false)
        {
            if (isNeedFuelNow &&
                (state.ContainerFuel.StateHash != state.ContainerFuelLastStateHash ||
                 forceRefreshFuel))
            {
                RefreshFuel(state, byproductsCraftQueue, config, objectManufacturer, isNeedFuelNow);
                state.ContainerFuelLastStateHash = state.ContainerFuel.StateHash;
            }

            var fuelUseTimeRemainsSeconds =
                state.FuelUseTimeRemainsSeconds
                + state.FuelUseTimeAccumulatedRemainder
                - deltaTime;

            if (fuelUseTimeRemainsSeconds <= 0)
            {
                // fuel is burned
                state.FuelUseTimeAccumulatedRemainder += state.FuelUseTimeRemainsSeconds;
                state.FuelUseTimeRemainsSeconds        = 0;

                if (isNeedFuelNow)
                {
                    // refresh fuel
                    RefreshFuel(state, byproductsCraftQueue, config, objectManufacturer, isNeedFuelNow);
                    return;
                }

                return;
            }

            // subtract fuel burn time
            state.FuelUseTimeAccumulatedRemainder = 0;
            state.FuelUseTimeRemainsSeconds       = fuelUseTimeRemainsSeconds;

            if (config.IsProduceByproducts)
            {
                if (byproductsCraftQueue is null)
                {
                    throw new Exception("No byproductsCraftQueue");
                }

                CraftingMechanics.ServerUpdate(byproductsCraftQueue,
                                               deltaTime * byproductsQueueRate);
            }
        }
        /// <summary>
        /// Consumes fuel and populate remaining fuel burning time when needed.
        /// </summary>
        private static void RefreshFuel(
            FuelBurningState state,
            CraftingQueue byproductsCraftQueue,
            ManufacturingConfig config,
            IStaticWorldObject objectManufacturer,
            bool isNeedFuelNow)
        {
            if (state.FuelUseTimeRemainsSeconds > 0 ||
                !isNeedFuelNow)
            {
                return;
            }

            // look for best fuel item from placed into the fuel container
            var bestFuelItem = state.ContainerFuel.Items.OrderBy(
                i =>
            {
                var fuelItemType = i.ProtoItem as IProtoItemFuelSolid;
                return(fuelItemType?.FuelAmount ?? double.MaxValue);
            })
                               .FirstOrDefault();

            var bestFuelItemType = bestFuelItem?.ProtoItem as IProtoItemFuelSolid;

            if (bestFuelItemType == null)
            {
                // no fuel placed in fuel container
                //if (state.CurrentFuelItemType != null)
                //{
                //    state.CurrentFuelItemType = null;
                //    Logger.Info($"Fuel depleted for manufacturing at {objectManufacturer}");
                //}

                return;
            }

            Logger.Info($"Fuel will be used for manufacturing {bestFuelItemType} at {objectManufacturer}");

            // destroy one fuel item
            Api.Server.Items.SetCount(bestFuelItem, bestFuelItem.Count - 1);

            // set fuel burn time
            state.CurrentFuelItemType = bestFuelItemType;
            var secondsToBurn = bestFuelItemType.FuelAmount;

            state.FuelUseTimeRemainsSeconds = secondsToBurn;

            OnFuelChanged(objectManufacturer, state, byproductsCraftQueue, config);
        }
        /// <summary>
        /// Selects recipe for manufacturing.
        /// </summary>
        public static void SelectRecipe(
            Recipe recipe,
            IStaticWorldObject objectManufacturer,
            ManufacturingState state,
            ManufacturingConfig config)
        {
            if (state.SelectedRecipe == recipe)
            {
                return;
            }

            state.SelectedRecipe = recipe;
            state.CraftingQueue.QueueItems.Clear();

            RefreshRecipe(state, config, objectManufacturer);
            //RefreshFuel(state, config, objectManufacturer);
        }