Иконка ресурса

FREE AutoComposter : 1.0.0 1.0.0

Нет прав для скачивания
C#:
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using System;
using UnityEngine;
using System.Diagnostics;
using SilentOrbit.ProtocolBuffers;
using Oxide.Game.Rust.Cui;
using static ConsoleSystem;

namespace Oxide.Plugins
{
    [Info("Auto Composter", "D3m3ntor", "1.0.0")]
    [Description("Allow composters to collect dungs from the floor and compost it efficiently by splitting.")]
    class AutoComposter : RustPlugin
    {
        #region Variables

        public static AutoComposter _instance;
        public const string PermissionKey = "autocomposter.use";
        public const string PermissionKeyPerf = "autocomposter.performance";
        public List<string> playersEnabled = new List<string>();
        public List<string> playersDisabled = new List<string>();
        public List<TimeSpan> dungDropPerformanceDebug = new List<TimeSpan>();

        #endregion

        #region Configuration

        public static Configuration _config;
        public class Configuration
        {
            [JsonProperty(PropertyName = "Use Only If Permission")]
            public bool UseOnlyIfPermission = false;

            [JsonProperty(PropertyName = "Turned on for players by default")]
            public bool TurnedOnByDefault = false;

            [JsonProperty(PropertyName = "Collect in radius")]
            public float CollectInRadius = 20;

            [JsonProperty(PropertyName = "Frequency of Composters Spliting")]
            public float SplitingFreq = 3f;

            [JsonProperty(PropertyName = "Composter Pickup sound ON")]
            public bool ComposterGrabDungPlaySound = true;

            [JsonProperty(PropertyName = "Composter pickup sound prefab")]
            public string ComposterGrabDungPrefab = "assets/bundled/prefabs/fx/collect/collect mushroom.prefab";

            [JsonProperty(PropertyName = "Debug")]
            public bool Debug = false;
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["enabled"] = "AutoComposter <color=#7FFF00>enabled</color>.",
                ["disabled"] = "AutoComposter <color=#ff0000>disabled</color>.",
                ["turnon"] = "Turn On",
                ["turnoff"] = "Turn Off",
                ["autocomposter"] = "AutoComposter",
                ["info"] = ("* Consolidates fertilizer and non-optimal compostable stacks.\n" +
                        "* Distributes the best compostable items across available slots.\n" +
                        "* Collects falling dung within a range of {0}m."),
                ["noAccess"] = "You do not have an <color=#ff0000>access</color> to this command.",
                ["perfInDebug"] = "Performance check is avaiable only in <color=#ff0000>debug</color> mode.",
            }, this);
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            _config = Config.ReadObject<Configuration>();
            if (_config == null)
            {
                PrintError("Your AutoComposter configuration file contains an error. Using default configuration values.");
                LoadDefaultConfig();
                SaveConfig();
            }
        }

        protected override void LoadDefaultConfig() => _config = new Configuration();
        protected override void SaveConfig() => Config.WriteObject(_config);

        private void SaveData()
        {
            if (_config.TurnedOnByDefault)
            {
                Interface.Oxide.DataFileSystem.WriteObject(Name + "/PlayersDisabled", playersDisabled);
                return;
            }

            Interface.Oxide.DataFileSystem.WriteObject(Name + "/PlayersEnabled", playersEnabled);
        }

        private void LoadData()
        {
            List<string> fileList;
            if (_config.TurnedOnByDefault)
            {
                fileList = Interface.Oxide.DataFileSystem.ReadObject<List<string>>(Name + "/PlayersDisabled");
                playersDisabled = fileList ?? new List<string>();
            }
            else
            {
                fileList = Interface.Oxide.DataFileSystem.ReadObject<List<string>>(Name + "/PlayersEnabled");
                playersEnabled = fileList ?? new List<string>();
            }
        }
        #endregion

        #region commands
        [ConsoleCommand("autocomposter.toggle")]
        void ToggleAutoComposterCommand(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null)
                return;

            ToggleAutocomposter(player);
            if (HasRightToAction(player.UserIDString))
                CreateUI(player, IsActionOn(player.UserIDString));
        }

        [ChatCommand("autocomposter")]
        private void AutoComposterCmd(BasePlayer player, string command, string[] args)
        {
            if (player == null)
                return;

            if (args.Contains("performance"))
            {
                if (!permission.UserHasPermission(player.UserIDString, PermissionKeyPerf))
                {
                    Message(player, "noAccess");
                    return;
                }
                else if(!_config.Debug)
                {
                    Message(player, "perfInDebug");
                    return;
                }

                PrintOutPerformanceResults(player);
                return;
            }

            ToggleAutocomposter(player);
        }

        #endregion

        #region Hooks
        private void Init()
        {
            _instance = this;
            LoadData();
            LoadConfig();
            permission.RegisterPermission(PermissionKey, this);
            permission.RegisterPermission(PermissionKeyPerf, this);
            var composters = UnityEngine.Object.FindObjectsOfType<Composter>();
            Array.ForEach(composters, x => OnEntitySpawned(x));
        }

        private void Unload()
        {
            SaveData();
            var composters = UnityEngine.Object.FindObjectsOfType<Composter>();
            for (var i = 0; i < composters.Length; i++)
            {
                var composter = composters[i];
                if (composter == null || composter.IsDestroyed)
                    continue;

                var component = composter.gameObject.GetComponent<SplittingController>();
                if (component == null)
                    continue;

                component.Stop();
                UnityEngine.Object.Destroy(component);
            }
        }

        private void OnEntitySpawned(BaseNetworkable entity)
        {
            var composter = entity as Composter;
            if (composter == null)
                return;

            var component = composter.GetComponent<SplittingController>();
            if (component == null)
                component = composter.gameObject.AddComponent<SplittingController>();

            timer.Once(3f, () => component.Start());
        }

        private void OnLootEntity(BasePlayer player, BaseEntity entity)
        {
            var composter = entity as Composter;
            if (composter == null || player == null)
                return;

            if (HasRightToAction(player.UserIDString))
                CreateUI(player, IsActionOn(player.UserIDString));

            var component = composter.gameObject.GetComponent<SplittingController>();
            if (component == null)
                return;

            component.OpenedBy(player.net.ID.Value);
            PrintDebug($"Composter has been open by player with id {player.net.ID.ToString()}");
        }

        private void OnLootEntityEnd(BasePlayer player, BaseCombatEntity entity)
        {
            var composter = entity as Composter;
            if (composter == null || player == null)
                return;

            DestroyUI(player);

            var component = composter.gameObject.GetComponent<SplittingController>();
            if (component == null)
                return;

            component.ClosedBy(player.net.ID.Value);
            PrintDebug($"Composter has been closed by player with id {player?.net.ID.ToString()}");
        }

        void OnItemDropped(Item item, BaseEntity entity)
        {
            if (item == null || entity == null)
                return;

            var dung = entity as DroppedItem;
            if (dung == null || item.info.shortname != "horsedung")
                return;

            Stopwatch watch = null;
            if (_config.Debug)
                watch = Stopwatch.StartNew();

            MoveDungToNearbyComposter(item, dung);

            SavePerformanceTest(watch, dungDropPerformanceDebug);
        }

        #endregion

        #region Helpers

        public void MoveDungToNearbyComposter(Item item, DroppedItem dung)
        {
            PrintDebug($"Dung item dropped at position {dung.transform.position}.");
            List<Composter> composters = new List<Composter>();
            Vis.Entities(dung.transform.position, _config.CollectInRadius, composters);

            var composterWithMinAmountItems = composters
                .OrderBy(x => x.inventory.itemList.Where(z => z.info.shortname == item.info.shortname).Sum(y => y.amount))
                .FirstOrDefault();

            if (composterWithMinAmountItems == null)
            {
                PrintDebug("No composters found within radius.");
                return;
            }

            var ownerId = composterWithMinAmountItems.OwnerID.ToString();
            if (IsActionEnabledForPlayer(ownerId))
            {
                var fertilizers = composterWithMinAmountItems.inventory.itemList.Where(x => x.info.shortname == "fertilizer").ToList();
                var compostableItems = composterWithMinAmountItems.inventory.itemList.Where(x => x.info.shortname != "fertilizer").ToList();

                var component = composterWithMinAmountItems.GetComponent<SplittingController>();
                var avaiableSlots = component?.CalculateAvailableSlots(fertilizers, compostableItems, item);
                var dungStacks = composterWithMinAmountItems.inventory.itemList
                    .Where(x => x.info.shortname == item.info.shortname);
                var canStackDung = avaiableSlots * item.MaxStackable() >= dungStacks.Sum(x => x.amount) + item.amount;
                if (avaiableSlots <= dungStacks.Count() && !canStackDung)
                {
                    PrintDebug($"Composter could not stack dung:{canStackDung}, slots:{avaiableSlots}");
                    return;
                }

                Item itemx = ItemManager.Create(item.info, item.amount);
                if (itemx.MoveToContainer(composterWithMinAmountItems.inventory))
                {
                    item.RemoveFromWorld();
                    if (_config.ComposterGrabDungPlaySound)
                        Effect.server.Run(_config.ComposterGrabDungPrefab, composterWithMinAmountItems.transform.position, Vector3.zero, null, false);
                    return;
                }

                itemx.Remove();
            }
        }

        public bool IsActionOn(string playerId)
        {
            return _config.TurnedOnByDefault ? !playersDisabled.Contains(playerId) : playersEnabled.Contains(playerId);
        }

        public bool HasRightToAction(string playerId)
        {
            return !_config.UseOnlyIfPermission || (_config.UseOnlyIfPermission && !permission.UserHasPermission(playerId, PermissionKey));
        }

        public bool IsActionEnabledForPlayer(string playerId)
        {
            if (!HasRightToAction(playerId))
                return false;

            return IsActionOn(playerId);
        }

        public void ToggleAutocomposter(BasePlayer player)
        {
            if (_config.UseOnlyIfPermission && !permission.UserHasPermission(player.UserIDString, PermissionKey))
            {
                Message(player, "noAccess");
                return;
            }

            if (_config.TurnedOnByDefault)
            {
                if (!playersDisabled.Contains(player.UserIDString))
                {
                    playersDisabled.Add(player.UserIDString);
                    Message(player, "disabled");
                }
                else
                {
                    playersDisabled.Remove(player.UserIDString);
                    Message(player, "enabled");
                }
            }
            else
            {
                if (!playersEnabled.Contains(player.UserIDString))
                {
                    playersEnabled.Add(player.UserIDString);
                    Message(player, "enabled");
                }
                else
                {
                    playersEnabled.Remove(player.UserIDString);
                    Message(player, "disabled");
                }
            }

            SaveData();
        }

        public void PrintOutPerformanceResults(BasePlayer player)
        {
            var splitTests = GameObject.FindObjectsOfType<SplittingController>().Select(x => x.componentPerformanceDebug);
            var message = GetStatistics($"Splitting Dung in composters({splitTests.Count()})", splitTests.SelectMany(x => x).ToList());
            player.ChatMessage(message);

            message = GetStatistics("Dung Drop to composter", dungDropPerformanceDebug);
            player.ChatMessage(message);
        }

        public static string GetStatistics(string name, List<TimeSpan> timeSpans)
        {
            if (timeSpans == null || timeSpans.Count == 0)
            {
                return $"No data available for {name}.";
            }

            // Calculate statistics
            var minTime = timeSpans.Min();
            var maxTime = timeSpans.Max();
            var avgTime = TimeSpan.FromTicks((long)timeSpans.Average(ts => ts.Ticks));
            var medianTime = CalculateMedian(timeSpans);

            // Build the result string
            return $"Statistics of Execution Times for <color=#7FFF00>{name}</color>:\n" +
                $"- Count: {timeSpans.Count}\n" +
                $"- Min: {minTime.TotalMilliseconds} ms\n" +
                $"- Max: {maxTime.TotalMilliseconds} ms\n" +
                $"- Average: {avgTime.TotalMilliseconds} ms\n" +
                $"- Median: {medianTime.TotalMilliseconds} ms\n";
        }

        // Helper function to calculate the median of a list of TimeSpan
        private static TimeSpan CalculateMedian(List<TimeSpan> times)
        {
            if (times.Count == 0)
                return TimeSpan.Zero;

            var sortedTimes = times.OrderBy(t => t).ToList();
            int count = sortedTimes.Count;
            if (count % 2 == 0)
                return sortedTimes[count / 2];

            var mid1 = sortedTimes[(count / 2) - 1];
            var mid2 = sortedTimes[count / 2];
            return TimeSpan.FromTicks((mid1.Ticks + mid2.Ticks) / 2);

        }

        public static void SavePerformanceTest(Stopwatch stopwatch, List<TimeSpan> performanceList)
        {
            if (!_config.Debug)
                return;

            stopwatch.Stop();
            performanceList.Add(stopwatch.Elapsed);
        }

        public string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
        public void Message(BasePlayer player, string key, params object[] args) => player.ChatMessage(Lang(key, player.UserIDString, args));
        public static void PrintDebug(string message)
        {
            if (_config.Debug)
                UnityEngine.Debug.Log($"DEBUG AutoComposter > " + message);
        }

        #endregion

        #region UI
        void CreateUI(BasePlayer player, bool isOn)
        {
            DestroyUI(player);

            CuiElementContainer container = new CuiElementContainer();

            // Main Panel
            string mainPanel = container.Add(new CuiPanel
            {
                RectTransform = { AnchorMin = "0.65 0.36", AnchorMax = "0.947 0.55" },
                Image = { Color = "0.1 0.1 0.1 0.8" }
            }, "Overlay", "AutocomposterUI");

            string contentColor = "0.7 0.7 0.7 1.0";

            // Title Box
            container.Add(new CuiLabel
            {
                Text = { Text = Lang("autocomposter", player.UserIDString), Color = contentColor, FontSize = 16, Align = TextAnchor.MiddleCenter },
                RectTransform = { AnchorMin = "0 0.8", AnchorMax = "1 1" }
            }, mainPanel);

            string buttonColor = !isOn
                ? "0.415 0.5 0.258 0.4"
                : "0.8 0.254 0.254 0.4";
            string buttonTextColor = !isOn
                    ? "0.607 0.705 0.431"
                    : "0.705 0.607 0.431";
            string buttonText = !isOn
                ? Lang("turnon", player.UserIDString)
                : Lang("turnoff", player.UserIDString);

            container.Add(new CuiButton
            {
                Button = { Command = "autocomposter.toggle", Color = buttonColor },
                RectTransform = { AnchorMin = "0.75 0.8", AnchorMax = "0.95 0.95" },
                Text = { Text = buttonText, Color = buttonTextColor, FontSize = 14, Align = TextAnchor.MiddleCenter }
            }, mainPanel);

            // Body Content
            container.Add(new CuiLabel
            {
                Text = { Text = Lang("info", player.UserIDString, _config.CollectInRadius),
                FontSize = 12, Align = TextAnchor.MiddleLeft, Color = contentColor },
                RectTransform = { AnchorMin = "0.05 0", AnchorMax = "0.95 0.85" }
            }, mainPanel);

            CuiHelper.AddUi(player, container);
        }

        void DestroyUI(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, "AutocomposterUI");
        }
        #endregion

        #region Controller
        public class SplittingController : FacepunchBehaviour
        {
            public List<TimeSpan> componentPerformanceDebug = new List<TimeSpan>();

            private List<ulong> _playersHoldOpen = new List<ulong>();
            private Composter _composter;
            private Composter Composter
            {
                get
                {
                    if (_composter == null)
                        _composter = GetComponent<Composter>();

                    return _composter;
                }
            }

            public void Start()
            {
                Stop();
                Composter.InvokeRepeating(ComposterItemsSplit, _config.SplitingFreq, _config.SplitingFreq);
            }

            public void Stop()
            {
                Composter.CancelInvoke(ComposterItemsSplit);
            }

            public void OpenedBy(ulong playerId)
            {
                if(!_playersHoldOpen.Contains(playerId))
                {
                    _playersHoldOpen.Add(playerId);
                }
            }

            public void ClosedBy(ulong playerId)
            {
                if (_playersHoldOpen.Contains(playerId))
                {
                    _playersHoldOpen.Remove(playerId);
                }
            }

            void ComposterItemsSplit()
            {
                if (Composter.OwnerID == default(ulong) ||
                    !_instance.IsActionEnabledForPlayer(Composter.OwnerID.ToString()) ||
                    !Composter.inventory.itemList.Any())
                    return;

                var isComposterOpen = _playersHoldOpen.Any();
                if (isComposterOpen)
                {
                    PrintDebug($"Composter at pos {Composter.transform.position} is open, skipping.");
                    return;
                }
                Stopwatch stopwatch = null;
                if(_config.Debug)
                    stopwatch = Stopwatch.StartNew();

                var fertilizers = Composter.inventory.itemList.Where(item => item.info.shortname == "fertilizer").ToList();
                ConsolidateFertilizerStacks(fertilizers);

                var compostableItems = Composter.inventory.itemList.Where(item => item.info.shortname != "fertilizer").ToList();
                if (!compostableItems.Any())
                {
                    SavePerformanceTest(stopwatch, componentPerformanceDebug);
                    return;
                }

                var bestItem = compostableItems
                    .OrderByDescending(x => x.info.GetComponent<ItemModCompostable>().TotalFertilizerProduced)
                    .FirstOrDefault();

                var didChanges = ConsolidateCompostableItems(compostableItems, bestItem.info.shortname);
                if (didChanges) 
                {
                    SavePerformanceTest(stopwatch, componentPerformanceDebug);
                    return;
                }

                int availableSlots = CalculateAvailableSlots(fertilizers, compostableItems, bestItem);
                if (availableSlots <= 0)
                {
                    SavePerformanceTest(stopwatch, componentPerformanceDebug);
                    return;
                }

                DistributeBestItem(Composter, bestItem.info, availableSlots);
                SavePerformanceTest(stopwatch, componentPerformanceDebug);
            }

            public int CalculateAvailableSlots(List<Item> fertilizers, List<Item> compostableItems, Item bestItem)
            {
                var compostablesWithoutBest = compostableItems.Where(x => x.info.shortname != bestItem.info.shortname).ToList();
                if (!fertilizers.Any())
                    return Composter.inventory.capacity - compostablesWithoutBest.Count - 1;

                int totalFertilizer = fertilizers.Sum(f => f.amount);
                int maxStackSize = fertilizers.FirstOrDefault().MaxStackable();
                var fertilizerLeftToEndStacks = (maxStackSize * fertilizers.Count()) - totalFertilizer;
                float sumFertilizerProdWithoutBest = 0;
                var slotsTakenByNonBest = 0;

                foreach (var compostable in compostablesWithoutBest)
                {
                    if (compostable == null)
                        continue;

                    var fertiProductionOfItem = compostable.info.GetComponent<ItemModCompostable>().TotalFertilizerProduced;
                    sumFertilizerProdWithoutBest += fertiProductionOfItem;
                    slotsTakenByNonBest++;
                }

                var avaibleSlots = Composter.inventory.capacity - (fertilizers.Count() + slotsTakenByNonBest);
                var bestItemProductionOnEmptySlots = avaibleSlots * bestItem.info.GetComponent<ItemModCompostable>().TotalFertilizerProduced;
                var totalFertiOnTick = sumFertilizerProdWithoutBest + bestItemProductionOnEmptySlots;
                var ticksToFillUpFertiStacks = Math.Ceiling(fertilizerLeftToEndStacks / totalFertiOnTick);
                if (ticksToFillUpFertiStacks <= 2)
                    return avaibleSlots - 1;

                return avaibleSlots;
            }

            void DistributeBestItem(Composter composter, ItemDefinition bestItemInfo, int availableSlots)
            {
                if (availableSlots <= 0)
                    return;

                
                var bestItems = composter.inventory.itemList.Where(item => item.info.shortname == bestItemInfo.shortname);
                int totalAmountToDistribute = bestItems.Sum(item => item.amount);
                if (totalAmountToDistribute <= 0 || bestItems.Count() == availableSlots)
                    return;

                
                availableSlots = Math.Min(availableSlots, composter.inventory.capacity);

                
                int amountPerSlot = totalAmountToDistribute / availableSlots;
                int leftover = totalAmountToDistribute % availableSlots;

               
                for (int i = 0; i < composter.inventory.capacity; i++)
                {
                    var item = composter.inventory.GetSlot(i);
                    if (item == null || item?.info.shortname == bestItemInfo.shortname)
                    {
                        if (totalAmountToDistribute == 0 && item != null)
                        {
                            item.Remove();
                            break;
                        }

                        if (availableSlots <= 0 || (leftover == 0 && amountPerSlot == 0))
                            break;

                        int amountForThisSlot = amountPerSlot;
                        if (leftover > 0)
                        {
                            amountForThisSlot++;
                            leftover--;
                        }

                        if (item == null)
                        {
                            Item newItem = ItemManager.Create(bestItemInfo, amountForThisSlot);
                            var success = newItem.MoveToContainer(composter.inventory, i, ignoreStackLimit: true);
                            if (!success)
                            {
                                PrintDebug($"Could not move a new stack of {newItem.info.shortname}({amountForThisSlot}) on {i} slot for composter at pos {composter.transform.position}");
                                continue;
                            }
                            item = newItem;
                        }
                        else if (item.info.shortname == bestItemInfo.shortname)
                        {
                            item.amount = amountForThisSlot;
                        }

                        totalAmountToDistribute -= amountForThisSlot;
                        availableSlots--;
                    }
                }

                if (totalAmountToDistribute > 0)
                {
                    Item newItem = ItemManager.Create(bestItemInfo, totalAmountToDistribute);
                    newItem.MoveToContainer(composter.inventory);
                }

                composter.inventory.MarkDirty();
            }

            bool ConsolidateCompostableItems(List<Item> compostableItems, string bestItemName)
            {
                if (compostableItems.Select(x => x.info.shortname).Count() <= 1)
                    return false;

                var itemsAndSums = new Dictionary<string, int>();
                var itemsNotFullStacksAppearedTimes = new Dictionary<string, int>();
                for (int i = 0; i < Composter.inventory.capacity; i++)
                {
                    var item = Composter.inventory.GetSlot(i);
                    if (item == null || item.info.shortname == bestItemName)
                        continue;

                    var sum = itemsAndSums.ContainsKey(item.info.shortname) ? itemsAndSums[item.info.shortname] : 0;
                    itemsAndSums[item.info.shortname] = sum + item.amount;
                    var appearedTimes = itemsNotFullStacksAppearedTimes.ContainsKey(item.info.shortname) ? itemsNotFullStacksAppearedTimes[item.info.shortname] : 0;
                    if (item.amount < item.MaxStackable())
                        itemsNotFullStacksAppearedTimes[item.info.shortname] = appearedTimes + 1;
                }

                if (!itemsNotFullStacksAppearedTimes.Any(x => x.Value > 1))
                    return false;

                for (int i = 0; i < Composter.inventory.capacity; i++)
                {
                    var item = Composter.inventory.GetSlot(i);
                    if (item == null || item.info.shortname == bestItemName)
                        continue;

                    if (itemsAndSums[item.info.shortname] <= 0)
                    {
                        item.Remove();
                        continue;
                    }

                    if (item.amount >= item.MaxStackable())
                    {
                        itemsAndSums[item.info.shortname] -= item.MaxStackable();
                        continue;
                    }

                    var newAmount = Math.Min(item.MaxStackable(), itemsAndSums[item.info.shortname]);
                    item.amount = newAmount;
                    itemsAndSums[item.info.shortname] -= newAmount;
                }

                Composter.inventory.MarkDirty();
                return true;
            }

            void ConsolidateFertilizerStacks(List<Item> fertilizers)
            {
                fertilizers = fertilizers.OrderBy(f => f.amount).ToList();

                for (int i = 0; i < fertilizers.Count; i++)
                {
                    Item currentFertilizer = fertilizers[i];
                    if (currentFertilizer == null || currentFertilizer.amount >= currentFertilizer.MaxStackable())
                        continue;

                    for (int j = i + 1; j < fertilizers.Count; j++)
                    {
                        Item nextFertilizer = fertilizers[j];
                        if (nextFertilizer == null || nextFertilizer.amount >= nextFertilizer.MaxStackable())
                            continue;

                        int spaceAvailable = currentFertilizer.MaxStackable() - currentFertilizer.amount;
                        if (spaceAvailable <= 0)
                            break;

                        int amountToMove = Math.Min(spaceAvailable, nextFertilizer.amount);
                        currentFertilizer.amount += amountToMove;
                        nextFertilizer.amount -= amountToMove;

                        currentFertilizer.MarkDirty();
                        nextFertilizer.MarkDirty();

                        if (nextFertilizer.amount <= 0)
                        {
                            nextFertilizer.Remove();
                            fertilizers[j] = null;
                        }
                    }
                }
            }

        }
        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху