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

FREE BetterChinookPatrol : 0.2.1 0.2.1

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Globalization;
namespace Oxide.Plugins {
    [Info("Better Chinook Patrol", "WhiteThunder", "0.2.1")]
    [Description("Allows customizing which monuments chinooks will visit.")]
    internal class BetterChinookPatrol : CovalencePlugin {
#region Fields
        private const float VanillaDropZoneDistanceTolerance = 200;
        private Configuration _config;
        private List<Vector3> _eligiblePatrolPoints = new();
#endregion
#region Hooks
        private void Init() {
            _config.Init(this);
        }
        private void OnServerInitialized() {
            PrintDropZones();
        }
        private void OnEntitySpawned(CH47HelicopterAIController ch47) {
            // Ignore reinforcement chinooks.if (ch47.ShouldLand())
            return;
            var brain = ch47.GetComponent<CH47AIBrain>();
            if (brain == null) return;
            NextTick(() => {
                // If the brain doesn't have a path finder, perhaps another plugin is controlling it.if (brain.PathFinder is not CH47PathFinder pathFinder)
                return;
                if (ChinookWasBlocked(ch47)) return;
                brain.PathFinder = new BetterCH47PathFinder(_eligiblePatrolPoints);
                // If the chinook is already in a patrol state, its interest point must be updated.if (brain.CurrentState is { StateType: AIState.Patrol })
                {
                    brain.mainInterestPoint = brain.PathFinder.GetRandomPatrolPoint();
                }
                if (_config.MinCrateDropsPerChinook > 1 && _config.MaxCrateDropsPerChinook > 1) {
                    ch47.numCrates = UnityEngine.Random.Range(_config.MinCrateDropsPerChinook, _config.MaxCrateDropsPerChinook + 1);
                }
            }
            );
        }
        private void OnMonumentPrefabCreated(GameObject gameObject, Component monument, Guid guid) {
            if (gameObject.HasComponent<CH47DropZone>()) {
                Log($"Detected new Monument Addons drop zone at {gameObject.transform.position}");
            }
        }
#endregion
#region Exposed Hooks
        private bool ChinookWasBlocked(CH47HelicopterAIController ch47) {
            return Interface.CallHook("OnBetterChinookPatrol", ch47) is false;
        }
#endregion
#region Helpers
        private static class StringUtils {
            public static bool Equals(string a, string b) {
                return string.Compare(a, b, StringComparison.OrdinalIgnoreCase) == 0;
            }
            public static bool Contains(string haystack, string needle) {
                return haystack.Contains(needle, CompareOptions.IgnoreCase);
            }
        }
        private void PrintDropZones() {
            var sb = new StringBuilder();
            var dropZoneCount = 0;
            foreach (var monumentInfo in TerrainMeta.Path.Monuments) {
                if (_config.DisallowSafeZoneMonuments && monumentInfo.IsSafeZone) continue;
                if (!_config.AllowsMonument(monumentInfo, out var monumentName)) continue;
                var monumentPosition = monumentInfo.transform.position;
                _eligiblePatrolPoints.Add(monumentPosition);
                var hasDropZone = false;
                var closestDropZone = CH47DropZone.GetClosest(monumentPosition);
                if (closestDropZone != null) {
                    hasDropZone = Vector3Ex.Distance2D(closestDropZone.transform.position, monumentPosition) < VanillaDropZoneDistanceTolerance;
                }
                if (hasDropZone) {
                    dropZoneCount++;
                }
                var dropZoneInfo = hasDropZone ? " -- HAS DROP ZONE" : string.Empty;
                sb.AppendLine($"- {monumentName}{dropZoneInfo}");
            }
            Log($"{_eligiblePatrolPoints.Count} monuments on this map may be visited by Chinooks.{dropZoneCount} have drop zones.This list may not show drop zones managed by other plugins (such as Monument Addons), particularly if the drop zones have not spawned yet.\n{sb}");
        }
#endregion
#region Pathfinder
        private class BetterCH47PathFinder : CH47PathFinder {
            private const float RevisitMaxProximity = 100;
            public List<Vector3> _patrolPath;
            private int _patrolPathIndex;
            public BetterCH47PathFinder(List<Vector3> eligiblePatrolPoints) {
                // Randomly shuffle the patrol points._patrolPath = eligiblePatrolPoints.OrderBy(x => UnityEngine.Random.Range(0, 1000)).ToList();
                for (var i = _patrolPath.Count - 1; i >= 0; i--) {
                    for (var j = 0; j < i; j++) {
                        // Remove any patrol points that are close to previous patrol points.if (Vector3Ex.Distance2D(_patrolPath[i], _patrolPath[j]) < RevisitMaxProximity)
                        {
                            _patrolPath.RemoveAt(i);
                            break;
                        }
                    }
                }
            }
            public override Vector3 GetRandomPatrolPoint() {
                if (_patrolPath.Count == 0) return Vector3.zero;
                if (_patrolPathIndex >= _patrolPath.Count) {
                    _patrolPathIndex = 0;
                }
                return _patrolPath[_patrolPathIndex++];
            }
        }
#endregion
#region Configuration
        [JsonObject(MemberSerialization.OptIn)]
        private class Configuration : SerializableConfiguration {
            [JsonIgnore]
            public List<MonumentType> DisallowedMonumentTypes = new();
            [JsonIgnore]
            public MonumentTier DisallowedMonumentTiersMask;
            [JsonProperty("Min crate drops per chinook")]
            public int MinCrateDropsPerChinook = 1;
            [JsonProperty("Max crate drops per chinook")]
            public int MaxCrateDropsPerChinook = 1;
            [JsonProperty("Disallow safe zone monuments")]
            public bool DisallowSafeZoneMonuments = true;
            [JsonProperty("Disallowed monument types")]
            private string[] DisallowedMonumentTypesNames = {
                "Cave", "WaterWell",
            }
            ;
            [JsonProperty("Disallowed monument tiers")]
            private string[] DisallowedMonumentTierNames = {
                "Tier0"
            }
            ;
            [JsonProperty("Disallowed monument prefabs (partial match)")]
            private string[] DisallowedMonumentPartialPrefabs = Array.Empty<string>();
            [JsonProperty("Disallowed monument prefabs (exact match)")]
            private string[] DisallowedMonumentExactPrefabs = Array.Empty<string>();
            [JsonProperty("Force allow monument prefabs (partial match)")]
            private string[] ForceAllowedMonumentPartialPrefabs = Array.Empty<string>();
            [JsonProperty("Force allow monument prefabs (exact match)")]
            private string[] ForceAllowedMonumentExactPrefabs = Array.Empty<string>();
            public void Init(BetterChinookPatrol pluginInstance) {
                if (DisallowedMonumentTypesNames != null) {
                    foreach (var monumentTypeName in DisallowedMonumentTypesNames) {
                        if (Enum.TryParse(monumentTypeName, ignoreCase : true, result : out MonumentType monumentType)) {
                            DisallowedMonumentTypes.Add(monumentType);
                        }
                        else {
                            pluginInstance.LogError($"Invalid monument type: {monumentTypeName}");
                        }
                    }
                }
                if (DisallowedMonumentTierNames != null) {
                    foreach (var monumentTierName in DisallowedMonumentTierNames) {
                        if (Enum.TryParse(monumentTierName, ignoreCase : true, result : out MonumentTier monumentTier)) {
                            DisallowedMonumentTiersMask |= monumentTier;
                        }
                        else {
                            pluginInstance.LogError($"Invalid monument tier: {monumentTierName}");
                        }
                    }
                }
            }
            public bool AllowsMonument(MonumentInfo monumentInfo, out string monumentName) {
                monumentName = monumentInfo.name;
                if (monumentName.Contains("monument_marker.prefab")) {
                    monumentName = monumentInfo.transform.root.name;
                }
                if (ForceAllowedMonumentPartialPrefabs != null) {
                    foreach (var partialPrefab in ForceAllowedMonumentPartialPrefabs) {
                        if (!string.IsNullOrWhiteSpace(partialPrefab) && StringUtils.Contains(monumentName, partialPrefab)) return true;
                    }
                }
                if (ForceAllowedMonumentExactPrefabs != null) {
                    foreach (var exactPrefab in ForceAllowedMonumentExactPrefabs) {
                        if (!string.IsNullOrWhiteSpace(exactPrefab) && StringUtils.Equals(monumentName, exactPrefab)) return true;
                    }
                }
                if (DisallowedMonumentPartialPrefabs != null) {
                    foreach (var partialPrefab in DisallowedMonumentPartialPrefabs) {
                        if (!string.IsNullOrWhiteSpace(partialPrefab) && StringUtils.Contains(monumentName, partialPrefab)) return false;
                    }
                }
                if (DisallowedMonumentExactPrefabs != null) {
                    foreach (var partialPrefab in DisallowedMonumentExactPrefabs) {
                        if (!string.IsNullOrWhiteSpace(partialPrefab) && StringUtils.Equals(monumentName, partialPrefab)) return false;
                    }
                }
                if ((DisallowedMonumentTiersMask & monumentInfo.Tier) != 0) return false;
                if (DisallowedMonumentTypes.Contains(monumentInfo.Type)) return false;
                return true;
            }
        }
        private Configuration GetDefaultConfig() => new();
#region Configuration Helpers
        private class SerializableConfiguration {
            public string ToJson() => JsonConvert.SerializeObject(this);
            public Dictionary<string, object> ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary<string, object>;
        }
        private static class JsonHelper {
            public static object Deserialize(string json) => ToObject(JToken.Parse(json));
            private static object ToObject(JToken token) {
                switch (token.Type) {
                    case JTokenType.Object : return token.Children<JProperty>().ToDictionary(prop => prop.Name, prop => ToObject(prop.Value));
                    case JTokenType.Array : return token.Select(ToObject).ToList();
                    default : return ((JValue)token).Value;
                }
            }
        }
        private bool MaybeUpdateConfig(SerializableConfiguration config) {
            var currentWithDefaults = config.ToDictionary();
            var currentRaw = Config.ToDictionary(x => x.Key, x => x.Value);
            return MaybeUpdateConfigDict(currentWithDefaults, currentRaw);
        }
        private bool MaybeUpdateConfigDict(Dictionary<string, object> currentWithDefaults, Dictionary<string, object> currentRaw) {
            bool changed = false;
            foreach (var key in currentWithDefaults.Keys) {
                if (currentRaw.TryGetValue(key, out var currentRawValue)) {
                    var currentDictValue = currentRawValue as Dictionary<string, object>;
                    if (currentWithDefaults[key] is Dictionary<string, object> defaultDictValue) {
                        if (currentDictValue == null) {
                            currentRaw[key] = currentWithDefaults[key];
                            changed = true;
                        }
                        else if (MaybeUpdateConfigDict(defaultDictValue, currentDictValue)) {
                            changed = true;
                        }
                    }
                }
                else {
                    currentRaw[key] = currentWithDefaults[key];
                    changed = true;
                }
            }
            return changed;
        }
        protected override void LoadDefaultConfig() => _config = GetDefaultConfig();
        protected override void LoadConfig() {
            base.LoadConfig();
            try {
                _config = Config.ReadObject<Configuration>();
                if (_config == null) throw new JsonException();
                if (MaybeUpdateConfig(_config)) {
                    LogWarning("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
            }
            catch (Exception e) {
                LogError(e.Message);
                LogWarning($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
        }
        protected override void SaveConfig() {
            Log($"Configuration changes saved to {Name}.json");
            Config.WriteObject(_config, true);
        }
#endregion
#endregion
    }
}

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