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

FREE BetterDroppedItemStacker : 1.7.0 1.7.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
/*
 * Copyright (C) 2024 Game4Freak.io
 * Your use of this mod indicates acceptance of the Game4Freak EULA.* Full legal terms can be found at https://game4freak.io/eula/
 */

using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Rust;
using System.Collections.Generic;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Better Dropped Item Stacker", "VisEntities", "1.7.0")]
    [Description("Combines scattered dropped items into one container.")]
    public class BetterDroppedItemStacker : RustPlugin
    {
        #region Fields

        private static BetterDroppedItemStacker _plugin;
        private static Configuration _config;

        private List<Timer> _pendingGroupTimers = new List<Timer>();

        private const int LAYER_ITEM_DROP = Layers.Mask.Ragdoll;
        private const int LAYER_DROPPED_ITEM = Layers.Mask.Physics_Debris;        
        private const int LAYER_OBSTACLES = Layers.Mask.Construction | Layers.Mask.Terrain | Layers.Mask.World;
        
        private const string PREFAB_ITEM_DROP = "assets/prefabs/misc/item drop/item_drop.prefab";

        #endregion Fields

        #region Configuration

        private class Configuration
        {
            [JsonProperty("Version")]
            public string Version { get; set; }

            [JsonProperty("Time Delay Before Grouping Items Seconds")]
            public float TimeDelayBeforeGroupingItemsSeconds { get; set; }
            
            [JsonProperty("Minimum Nearby Items Required To Group")]
            public int MinimumNearbyItemsRequiredToGroup { get; set; }

            [JsonProperty("Item Detection Radius")]
            public float ItemDetectionRadius { get; set; }

            [JsonProperty("Container Lifetime Seconds (0=auto-calculate)")]
            public float ContainerLifetimeSeconds { get; set; }

            [JsonProperty("Group Into Existing Containers")]
            public bool GroupIntoExistingContainers { get; set; }

            [JsonProperty("Item Categories To Ignore During Grouping")]
            public List<string> ItemCategoriesToIgnoreDuringGrouping { get; set; }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            _config = Config.ReadObject<Configuration>();

            if (string.Compare(_config.Version, Version.ToString()) < 0)
                UpdateConfig();

            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            _config = GetDefaultConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(_config, true);
        }

        private void UpdateConfig()
        {
            PrintWarning("Config changes detected! Updating...");

            Configuration defaultConfig = GetDefaultConfig();

            if (string.Compare(_config.Version, "1.0.0") < 0)
                _config = defaultConfig;

            if (string.Compare(_config.Version, "1.6.0") < 0)
                _config = defaultConfig;

            if (string.Compare(_config.Version, "1.7.0") < 0)
            {
                _config.GroupIntoExistingContainers = defaultConfig.GroupIntoExistingContainers;
            }

            PrintWarning("Config update complete! Updated from version " + _config.Version + " to " + Version.ToString());
            _config.Version = Version.ToString();
        }

        private Configuration GetDefaultConfig()
        {
            return new Configuration
            {
                Version = Version.ToString(),
                MinimumNearbyItemsRequiredToGroup = 5,
                TimeDelayBeforeGroupingItemsSeconds = 5,
                ItemDetectionRadius = 4f,
                ContainerLifetimeSeconds = 600f,
                GroupIntoExistingContainers = true,
                ItemCategoriesToIgnoreDuringGrouping = new List<string>
                {
                    "Weapon",
                    "Ammunition"
                }
            };
        }

        #endregion Configuration

        #region Oxide Hooks
        
        private void Init()
        {
            _plugin = this;
        }

        private void Unload()
        {
            foreach (Timer timer in _pendingGroupTimers)
            {
                if (timer != null)
                    timer.Destroy();
            }

            _config = null;
            _plugin = null;
        }

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

            BasePlayer ownerPlayer = item.GetOwnerPlayer();
            if (ownerPlayer == null)
                return;

            if (ownerPlayer.InSafeZone())
                return;

            Timer itemGroupingTimer = timer.Once(_config.TimeDelayBeforeGroupingItemsSeconds, () =>
            {
                if (worldEntity == null)
                    return;

                List<DroppedItem> nearbyDroppedItems = GetNearbyDroppedItems(worldEntity.transform.position, _config.ItemDetectionRadius);
                if (nearbyDroppedItems.Count >= _config.MinimumNearbyItemsRequiredToGroup)
                {
                    DroppedItemContainer container = null;
                    if (_config.GroupIntoExistingContainers)
                    {
                        container = FindExistingDroppedItemContainer(worldEntity.transform.position, 2f);
                    }
                    if (container == null && TerrainUtil.GetGroundInfo(worldEntity.transform.position, out RaycastHit raycastHit, 1f, LAYER_OBSTACLES))
                    {
                        container = SpawnDroppedItemContainer(raycastHit.point, Quaternion.FromToRotation(Vector3.up, raycastHit.normal), nearbyDroppedItems.Count);
                    }
                    if (container != null)
                    {
                        GroupItemsIntoContainer(container, nearbyDroppedItems);
                    }
                }

                Pool.FreeUnmanaged(ref nearbyDroppedItems);
            });

            _pendingGroupTimers.Add(itemGroupingTimer);
        }

        #endregion Oxide Hooks

        #region Container Spawning and Setup

        private DroppedItemContainer SpawnDroppedItemContainer(Vector3 position, Quaternion rotation, int capacity)
        {
            DroppedItemContainer container = GameManager.server.CreateEntity(PREFAB_ITEM_DROP, position, rotation) as DroppedItemContainer;
            if (container == null)
                return null;

            container.inventory = new ItemContainer();
            container.inventory.ServerInitialize(null, capacity);
            container.inventory.GiveUID();
            container.inventory.entityOwner = container;

            container.Spawn();

            float despawnTime = container.CalculateRemovalTime();
            if (_config.ContainerLifetimeSeconds > 0)
            {
                despawnTime = _config.ContainerLifetimeSeconds;
            }
            container.ResetRemovalTime(despawnTime);

            return container;
        }

        private DroppedItemContainer FindExistingDroppedItemContainer(Vector3 position, float searchRadius)
        {
            List<DroppedItemContainer> nearbyContainers = Pool.Get<List<DroppedItemContainer>>();
            Vis.Entities(position, searchRadius, nearbyContainers, LAYER_ITEM_DROP, QueryTriggerInteraction.Ignore);

            foreach (DroppedItemContainer container in nearbyContainers)
            {
                if (container != null && TerrainUtil.HasLineOfSight(position, container.transform.position, LAYER_OBSTACLES))
                {
                    Pool.FreeUnmanaged(ref nearbyContainers);
                    return container;
                }
            }
            Pool.FreeUnmanaged(ref nearbyContainers);
            return null;
        }

        #endregion Container Spawning and Setup

        #region Item Grouping

        private void GroupItemsIntoContainer(DroppedItemContainer container, List<DroppedItem> items)
        {
            int currentSlotCount = container.inventory.itemList.Count;
            int requiredSlots = items.Count;
            if (currentSlotCount + requiredSlots > container.inventory.capacity)
            {
                int newCapacity = currentSlotCount + requiredSlots;
                container.inventory.capacity = newCapacity;
            }

            foreach (DroppedItem droppedItem in items)
            {
                droppedItem.item.MoveToContainer(container.inventory);
            }

            float newDespawnTime = container.CalculateRemovalTime();
            if (_config.ContainerLifetimeSeconds > 0)
            {
                newDespawnTime = _config.ContainerLifetimeSeconds;
            }
            container.ResetRemovalTime(newDespawnTime);

            ExposedHooks.OnDroppedItemsGrouped(container, items);
        }

        #endregion Item Grouping

        #region Nearby Items Retrieval

        private List<DroppedItem> GetNearbyDroppedItems(Vector3 position, float radius)
        {
            List<DroppedItem> droppedItems = Pool.Get<List<DroppedItem>>();
            Vis.Entities(position, radius, droppedItems, LAYER_DROPPED_ITEM, QueryTriggerInteraction.Ignore);

            for (int i = droppedItems.Count - 1; i >= 0; i--)
            {
                DroppedItem droppedItem = droppedItems[i];
                if (droppedItem == null || droppedItem.ShortPrefabName != "generic_world"
                    || _config.ItemCategoriesToIgnoreDuringGrouping.Contains(droppedItem.item.info.category.ToString())
                    || !TerrainUtil.HasLineOfSight(position, droppedItem.transform.position, LAYER_OBSTACLES))
                {
                    droppedItems.RemoveAt(i);
                }
            }

            return droppedItems;
        }

        #endregion Nearby Items Retrieval

        #region Exposed Hooks

        private static class ExposedHooks
        {
            public static void OnDroppedItemsGrouped(DroppedItemContainer container, List<DroppedItem> groupedItems)
            {
                Interface.CallHook("OnDroppedItemsGrouped", container, groupedItems);
            }
        }

        #endregion Exposed Hooks

        #region Helper Classes

        public static class TerrainUtil
        {
            public static bool HasLineOfSight(Vector3 startPosition, Vector3 endPosition, LayerMask mask)
            {
                return GamePhysics.LineOfSight(startPosition, endPosition, mask);
            }

            public static bool GetGroundInfo(Vector3 startPosition, out RaycastHit raycastHit, float range, LayerMask mask)
            {
                return Physics.Linecast(startPosition + new Vector3(0.0f, range, 0.0f), startPosition - new Vector3(0.0f, range, 0.0f), out raycastHit, mask);
            }

            public static bool GetGroundInfo(Vector3 startPosition, out RaycastHit raycastHit, float range, LayerMask mask, Transform ignoreTransform = null)
            {
                startPosition.y += 0.25f;
                range += 0.25f;
                raycastHit = default;

                RaycastHit hit;
                if (!GamePhysics.Trace(new Ray(startPosition, Vector3.down), 0f, out hit, range, mask, QueryTriggerInteraction.UseGlobal, null))
                    return false;

                if (ignoreTransform != null && hit.collider != null
                    && (hit.collider.transform == ignoreTransform || hit.collider.transform.IsChildOf(ignoreTransform)))
                {
                    return GetGroundInfo(startPosition - new Vector3(0f, 0.01f, 0f), out raycastHit, range, mask, ignoreTransform);
                }

                raycastHit = hit;
                return true;
            }
        }

        #endregion Helper Classes
    }
}

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