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

FREE PowerSpawn : 1.4.0 1.4.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#if DEBUG
using System.Diagnostics;
using Debug = UnityEngine.Debug;
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Rust;
using UnityEngine;
using Random = System.Random;
namespace Oxide.Plugins;
[Info("Power Spawn", "misticos", "1.4.0")]
[Description("Powerful position generation tool with API")]
internal class PowerSpawn : CovalencePlugin {
#region Variables
    private static PowerSpawn _ins;
    private readonly Random _random = new();
    private int _halfWorldSize;
    private const string PermissionLocation = "powerspawn.location";
#endregion
#region Configuration
    private Configuration _config;
    private class Configuration {
        [JsonProperty("Profiles", ObjectCreationHandling = ObjectCreationHandling.Replace)]
        public Dictionary<string, Profile> Profiles {
            get;
            set;
        }
        = new() {
            {
                string.Empty, new Profile()
            }
            , {
                "No Winter", new Profile {
                    BiomesBlocked = new List<TerrainBiome.Enum> {
                        TerrainBiome.Enum.Arctic
                    }
                    , SplatBlocked = new List<TerrainSplat.Enum> {
                        TerrainSplat.Enum.Snow
                    }
                }
            }
            , {
                "Beach Only", new Profile {
                    SplatBlocked = new List<TerrainSplat.Enum> {
                        TerrainSplat.Enum.Snow
                    }
                    , TopologiesAllowed = new List<TerrainTopology.Enum> {
                        TerrainTopology.Enum.Beach, TerrainTopology.Enum.Beachside
                    }
                }
            }
            , {
                "Snowy Forest", new Profile {
                    SplatThresholdMinimum = new Dictionary<TerrainSplat.Enum, float> {
                        {
                            TerrainSplat.Enum.Snow, 0.8f
                        }
                    }
                    , TopologiesAllowed = new List<TerrainTopology.Enum> {
                        TerrainTopology.Enum.Forest, TerrainTopology.Enum.Forestside
                    }
                }
            }
        }
        ;
        [JsonProperty("Respawn Configurations", ObjectCreationHandling = ObjectCreationHandling.Replace)]
        public List<RespawnConfiguration> RespawnConfigurations {
            get;
            set;
        }
        = new() {
            new RespawnConfiguration()
        }
        ;
        [JsonProperty("Respawn Profile", NullValueHandling = NullValueHandling.Ignore)]
        public string RespawnProfileName {
            get;
            set;
        }
        = null;
        [JsonProperty("Respawn Locations Group", NullValueHandling = NullValueHandling.Ignore)]
        public int? RespawnGroup {
            get;
            set;
        }
        = null;
        [JsonProperty("Enable Respawn Locations Group", NullValueHandling = NullValueHandling.Ignore)]
        public bool? EnableRespawnGroup {
            get;
            set;
        }
        = null;
        [JsonProperty("Enable Respawn Management")]
        public bool EnableRespawn {
            get;
            set;
        }
        = true;
        [JsonProperty("Location Management Commands", ObjectCreationHandling = ObjectCreationHandling.Replace)]
        public string[] LocationCommand {
            get;
            set;
        }
        = {
            "loc", "location", "ps"
        }
        ;
        public class RespawnConfiguration {
            [JsonProperty("Permission")]
            public string Permission {
                get;
                set;
            }
            = string.Empty;
            [JsonProperty("Profile Name")]
            public string ProfileName {
                get;
                set;
            }
            = string.Empty;
            [JsonProperty("Locations Group")]
            public int? Group {
                get;
                set;
            }
            = null;
            [JsonIgnore]
            public Profile Profile = null;
        }
        public class Profile {
            [JsonProperty("Minimal Distance To Building")]
            public int DistanceBuilding {
                get;
                set;
            }
            = 16;
            [JsonProperty("Minimal Distance To Collider")]
            public int DistanceCollider {
                get;
                set;
            }
            = 8;
            [JsonProperty("Raycast Distance Above")]
            public float DistanceRaycast {
                get;
                set;
            }
            = 50f;
            [JsonProperty("Number Of Attempts To Find A Position Per Frame")]
            public int AttemptsPerFrame {
                get;
                set;
            }
            = 160;
            [JsonProperty("Number Of Positions Per Frame")]
            public int PositionsPerFrame {
                get;
                set;
            }
            = 16;
            [JsonProperty("Number Of Attempts To Find A Pregenerated Position")]
            public int AttemptsPregenerated {
                get;
                set;
            }
            = 400;
            [JsonProperty("Pregenerated Positions Amount")]
            public int PregeneratedAmount {
                get;
                set;
            }
            = 4000;
            [JsonProperty("Pregenerated Amount Check Frequency (Seconds)")]
            public float PregeneratedCheck {
                get;
                set;
            }
            = 90f;
            [JsonProperty("Biomes Threshold", NullValueHandling = NullValueHandling.Ignore)]
            public Dictionary<TerrainBiome.Enum, float> BiomesThresholdOld {
                get;
                set;
            }
            = null;
            [JsonProperty("Biomes Minimum Threshold", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public Dictionary<TerrainBiome.Enum, float> BiomesThresholdMinimum {
                get;
                set;
            }
            = new();
            [JsonProperty("Biomes Maximum Threshold", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public Dictionary<TerrainBiome.Enum, float> BiomesThresholdMaximum {
                get;
                set;
            }
            = new();
            [JsonProperty("Biomes Allowed", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainBiome.Enum> BiomesAllowed {
                get;
                set;
            }
            = new();
            [JsonProperty("Biomes Blocked", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainBiome.Enum> BiomesBlocked {
                get;
                set;
            }
            = new();
            [JsonProperty("Splat Minimum Threshold", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public Dictionary<TerrainSplat.Enum, float> SplatThresholdMinimum {
                get;
                set;
            }
            = new();
            [JsonProperty("Splat Maximum Threshold", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public Dictionary<TerrainSplat.Enum, float> SplatThresholdMaximum {
                get;
                set;
            }
            = new();
            [JsonProperty("Splat Allowed", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainSplat.Enum> SplatAllowed {
                get;
                set;
            }
            = new();
            [JsonProperty("Splat Blocked", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainSplat.Enum> SplatBlocked {
                get;
                set;
            }
            = new();
            [JsonProperty("Topologies Allowed", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainTopology.Enum> TopologiesAllowed {
                get;
                set;
            }
            = new();
            [JsonProperty("Topologies Blocked", ObjectCreationHandling = ObjectCreationHandling.Replace, ItemConverterType = typeof(StringEnumConverter))]
            public List<TerrainTopology.Enum> TopologiesBlocked {
                get;
                set;
            }
            = new();
            [JsonIgnore]
            public Coroutine Coroutine = null;
#if DEBUG
            [JsonIgnore]
            public uint Calls = 0;
            [JsonIgnore]
            public double CallsTook = 0d;
            [JsonIgnore]
            public uint SkippedGenerations = 0;
#endif
            [JsonIgnore]
            public List<Vector3> Positions = new();
            public bool IsValidPosition(Vector3 position) {
                return IsValidColliders(position) && IsValidBuilding(position) && IsValidAbove(position) && IsValidTopology(position) && IsValidBiome(position) && IsValidSplat(position);
            }
            private const int LayerMaskAbove = ~(Layers.Terrain | Layers.Server.Players);
            public bool IsValidAbove(Vector3 position) {
                // Casting from above.If done from below, you could be inside a collider which would not be hit
                position += new Vector3(0f, DistanceRaycast + Mathf.Epsilon, 0f);
                return !Physics.Raycast(position, Vector3.down, DistanceRaycast, LayerMaskAbove);
            }
            private const int LayerMaskBuilding = Layers.Construction;
            public bool IsValidBuilding(Vector3 position) {
                return !Physics.CheckSphere(position, DistanceBuilding, LayerMaskBuilding);
            }
            // TODO: Separate option for triggers, perhaps per-mask even
            private const int LayerMaskColliders = ~(Layers.Terrain | Layers.Server.Players | Layers.Construction);
            public bool IsValidColliders(Vector3 position) {
                return !Physics.CheckSphere(position, DistanceCollider, LayerMaskColliders);
            }
            public bool IsValidTopology(Vector3 position) {
                // TODO: Instead of going through all topologies, make two masks and compare with bitwise operators
                if (TopologiesAllowed.Count > 0) {
                    foreach (var topology in TopologiesAllowed) if (TerrainMeta.TopologyMap.GetTopology(position, (int)topology)) return true;
                    return false;
                }
                if (TopologiesBlocked.Count > 0) {
                    foreach (var topology in TopologiesBlocked) if (TerrainMeta.TopologyMap.GetTopology(position, (int)topology)) return false;
                    return true;
                }
                return true;
            }
            public bool IsValidBiome(Vector3 position) {
                if (BiomesAllowed.Count > 0 || BiomesBlocked.Count > 0) {
                    var biomeHighest = (TerrainBiome.Enum)TerrainMeta.BiomeMap.GetBiomeMaxType(position);
                    if (BiomesAllowed.Count > 0 && !BiomesAllowed.Contains(biomeHighest)) return false;
                    if (BiomesBlocked.Count > 0 && BiomesBlocked.Contains(biomeHighest)) return false;
                }
                if (BiomesThresholdMinimum.Count > 0) {
                    foreach (var threshold in BiomesThresholdMinimum) if (TerrainMeta.BiomeMap.GetBiome(position, (int)threshold.Key) < threshold.Value) return false;
                }
                if (BiomesThresholdMaximum.Count > 0) {
                    foreach (var threshold in BiomesThresholdMaximum) if (TerrainMeta.BiomeMap.GetBiome(position, (int)threshold.Key) > threshold.Value) return false;
                }
                return true;
            }
            public bool IsValidSplat(Vector3 position) {
                if (SplatAllowed.Count > 0 || SplatBlocked.Count > 0) {
                    var splatHighest = (TerrainSplat.Enum)TerrainMeta.SplatMap.GetSplatMaxType(position);
                    if (SplatAllowed.Count > 0 && !SplatAllowed.Contains(splatHighest)) return false;
                    if (SplatBlocked.Count > 0 && SplatBlocked.Contains(splatHighest)) return false;
                }
                if (SplatThresholdMinimum.Count > 0) {
                    foreach (var threshold in SplatThresholdMinimum) if (TerrainMeta.SplatMap.GetSplat(position, (int)threshold.Key) < threshold.Value) return false;
                }
                if (SplatThresholdMaximum.Count > 0) {
                    foreach (var threshold in SplatThresholdMaximum) if (TerrainMeta.SplatMap.GetSplat(position, (int)threshold.Key) > threshold.Value) return false;
                }
                return true;
            }
        }
    }
    protected override void LoadConfig() {
        base.LoadConfig();
        try {
            _config = Config.ReadObject<Configuration>();
            if (_config == null) throw new Exception();
            if (_config.EnableRespawnGroup != null) {
                var respawnConfiguration = _config.RespawnConfigurations.FirstOrDefault(x => string.IsNullOrEmpty(x.Permission));
                if (respawnConfiguration == null) _config.RespawnConfigurations.Add(respawnConfiguration = new Configuration.RespawnConfiguration());
                respawnConfiguration.ProfileName = _config.RespawnProfileName;
                respawnConfiguration.Group = _config.EnableRespawnGroup.Value ? _config.RespawnGroup : null;
                _config.RespawnProfileName = null;
                _config.EnableRespawnGroup = null;
                _config.RespawnGroup = null;
            }
            foreach (var profile in _config.Profiles.Values) {
                if (profile.BiomesThresholdOld != null) {
                    profile.BiomesThresholdMinimum = profile.BiomesThresholdOld;
                    profile.BiomesThresholdOld = null;
                }
            }
            SaveConfig();
        }
        catch (Exception e) {
            PrintError("Your configuration file contains an error.Using default configuration values.\n" + e);
            LoadDefaultConfig();
        }
        if (_config == null) return;
        foreach (var configuration in _config.RespawnConfigurations) {
            configuration.Profile = _config.Profiles.GetValueOrDefault(configuration.ProfileName);
            if (configuration.Profile is null) throw new Exception($"Your configuration file contains an error.Profile '{configuration.ProfileName}' does not exist.");
        }
    }
    protected override void LoadDefaultConfig() => _config = new Configuration();
    protected override void SaveConfig() => Config.WriteObject(_config);
#endregion
#region Work with Data
    private PluginData _data = new();
    private class PluginData {
        public List<Location> Locations = new();
        [JsonIgnore]
        public Dictionary<int, List<Location>> LocationsByGroup = null;
        // ReSharper disable once MemberCanBePrivate.Local
        public int LastID = 0;
        public void AddLocation(Location location) {
            Locations.Add(location);
            if (!LocationsByGroup.TryGetValue(location.Group, out var groupLocations)) LocationsByGroup[location.Group] = groupLocations = new List<Location>();
            groupLocations.Add(location);
        }
        public void RemoveLocation(int index) {
            if (Locations.Count <= index) return;
            var location = Locations[index];
            var groupId = location.Group;
            if (LocationsByGroup.TryGetValue(groupId, out var groupLocations)) groupLocations.Remove(location);
            Locations.RemoveAt(index);
        }
        public class Location {
            public string Name;
            public int ID = _ins._data.LastID++;
            public int Group = -1;
            public Vector3 Position;
            public string Format(string player) {
                var text = new StringBuilder(GetMsg("Location: Format", player));
                text.Replace("{name}", Name);
                text.Replace("{id}", ID.ToString());
                text.Replace("{group}", Group.ToString());
                text.Replace("{position}", Position.ToString());
                return text.ToString();
            }
            public static int? FindIndexById(int id) {
                // TODO: this sucks
                for (var i = 0; i < _ins._data.Locations.Count; i++) {
                    if (_ins._data.Locations[i].ID == id) return i;
                }
                return null;
            }
            public static IReadOnlyList<Location> FindByGroup(int group) {
                // TODO: static sucks
                return _ins._data.LocationsByGroup.TryGetValue(group, out var locations) ? locations : Array.Empty<Location>();
            }
        }
    }
    private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, _data);
    private void LoadData() {
        try {
            _data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(Name);
        }
        catch (Exception e) {
            PrintError(e.ToString());
        }
        _data ??= new PluginData();
        _data.LocationsByGroup = _data.Locations.GroupBy(l => l.Group).ToDictionary(g => g.Key, g => g.ToList());
    }
#endregion
#region Hooks
    protected override void LoadDefaultMessages() {
        lang.RegisterMessages(new Dictionary<string, string> {
            {
                "No Permission", "nope"
            }
            , {
                "Location: Syntax", "Location Syntax:\n" + "new (Name) - Create a new location with a specified name\n" + "delete (ID) - Delete a location with the specified ID\n" + "edit (ID) <Parameter 1> <Value> <...> - Edit a location with the specified ID\n" + "update - Apply datafile changes\n" + "list - Get a list of locations\n" + "validate location (Profile Name) (ID) - Validate preconfigured location\n" + "validate generated (Profile Name) (ID) - Validate generated position (from draw)\n" + "validate position (Profile Name) - Validate your current position\n" + "debug <Profile Name> - Print minor debug information\n" + "show <Profile Name> <Radius> - Show all positions"
            }
            , {
                "Location: Edit Syntax", "Location Edit Parameters:\n" + "move (x;y;z / here) - Move a location to the specified position\n" + "group (ID / reset) - Set group of a location or reset the group"
            }
            , {
                "Location: Debug", "Currently available pre-generated positions: {amount}"
            }
            , {
                "Location: Shown", "Showing generated positions on screen.."
            }
            , {
                "Location: Unable To Parse Position", "Unable to parse the position"
            }
            , {
                "Location: Unable To Parse Group", "Unable to parse the entered group"
            }
            , {
                "Location: Format", "Location ID: {id}; Group: {group}; Position: {position}; Name: {name}"
            }
            , {
                "Location: Not Found", "Location was not found."
            }
            , {
                "Location: Generated Not Found", "Generated position was not found."
            }
            , {
                "Location: Profile Not Found", "Profile was not found."
            }
            , {
                "Location: Edit Finished", "Edit was finished."
            }
            , {
                "Location: Removed", "Location was removed from our database."
            }
            , {
                "Location: Updated", "Datafile changes were applied."
            }
            , {
                "Location: Validation Format", "Validation results:\n" + "Buildings: {buildings}\n" + "Colliders: {colliders}\n" + "Raycast above: {raycast}\n" + "Biomes: {biome}\n" + "Splat: {splat}\n" + "Topologies: {topology}"
            }
            , {
                "Location: Player Only", "This is available only to in-game players."
            }
        }
        , this);
    }
    private void Init() {
        _ins = this;
        LoadData();
        permission.RegisterPermission(PermissionLocation, this);
        AddCovalenceCommand(_config.LocationCommand, nameof(CommandLocation));
        if (!_config.EnableRespawn) Unsubscribe(nameof(OnPlayerRespawn));
        else {
            foreach (var configuration in _config.RespawnConfigurations) {
                if (!string.IsNullOrEmpty(configuration.Permission)) permission.RegisterPermission(configuration.Permission, this);
            }
        }
    }
    private void OnServerInitialized() {
        _halfWorldSize = ConVar.Server.worldsize / 2;
        foreach (var kvp in _config.Profiles) {
            kvp.Value.Coroutine = InvokeHandler.Instance.StartCoroutine(PositionGeneration(kvp.Value, kvp.Key));
        }
    }
    private void Unload() {
        foreach (var kvp in _config.Profiles) {
            InvokeHandler.Instance.StopCoroutine(kvp.Value.Coroutine);
        }
    }
    private object OnPlayerRespawn(BasePlayer player) {
        Configuration.RespawnConfiguration respawnConfiguration = null;
        foreach (var configuration in _config.RespawnConfigurations) {
            if (!string.IsNullOrEmpty(configuration.Permission) && !permission.UserHasPermission(player.UserIDString, configuration.Permission)) continue;
            respawnConfiguration = configuration;
            break;
        }
        if (respawnConfiguration == null) return null;
        Vector3? position = null;
        if (respawnConfiguration.Group is {
        }
        groupId) {
            var positions = PluginData.Location.FindByGroup(groupId);
            position = positions[_random.Next(0, positions.Count)].Position;
        }
        else if (respawnConfiguration.Profile is {
        }
        profile) {
            position = FindPregeneratedPosition(profile);
        }
        if (!position.HasValue) {
#if DEBUG
            Debug.Log($"{nameof(OnPlayerRespawn)} > Unable to find a position for {player.UserIDString}.");
#endif
            return null;
        }
#if DEBUG
        Debug.Log($"{nameof(OnPlayerRespawn)} > Found position for {player.UserIDString}: {position}.");
#endif
        return new BasePlayer.SpawnPoint {
            pos = position.Value
        }
        ;
    }
#endregion
#region Commands
    private void CommandLocation(IPlayer player, string command, string[] args) {
        if (!player.HasPermission(PermissionLocation)) {
            player.Reply(GetMsg("No Permission", player.Id));
            return;
        }
        if (args.Length == 0) {
            goto syntax;
        }
        switch (args[0]) {
            case "new" : case "n" : {
                if (args.Length != 2) {
                    goto syntax;
                }
                var location = new PluginData.Location {
                    Name = args[1]
                }
                ;
                player.Position(out location.Position.x, out location.Position.y, out location.Position.z);
                _data.AddLocation(location);
                player.Reply(location.Format(player.Id));
                goto saveData;
            }
            case "delete" : case "remove" : case "d" : case "r" : {
                if (args.Length != 2 || !int.TryParse(args[1], out var id)) {
                    goto syntax;
                }
                var locationIndex = PluginData.Location.FindIndexById(id);
                if (!locationIndex.HasValue) {
                    player.Reply(GetMsg("Location: Not Found", player.Id));
                    return;
                }
                _data.RemoveLocation(locationIndex.Value);
                player.Reply(GetMsg("Location: Removed", player.Id));
                goto saveData;
            }
            case "edit" : case "e" : {
                if (args.Length < 4 || !int.TryParse(args[1], out var id)) {
                    player.Reply(GetMsg("Location: Edit Syntax", player.Id));
                    return;
                }
                var locationIndex = PluginData.Location.FindIndexById(id);
                if (!locationIndex.HasValue) {
                    player.Reply(GetMsg("Location: Not Found", player.Id));
                    return;
                }
                var locationCd = new CommandLocationData {
                    Player = player, Location = _data.Locations[locationIndex.Value]
                }
                ;
                locationCd.Apply(args);
                player.Reply(GetMsg("Location: Edit Finished", player.Id));
                goto saveData;
            }
            case "update" : case "u" : {
                LoadData();
                player.Reply(GetMsg("Location: Updated", player.Id));
                return;
            }
            case "list" : case "l" : {
                var table = new TextTable();
                table.AddColumns("ID", "Name", "Group", "Position");
                foreach (var location in _data.Locations) {
                    table.AddRow(location.ID.ToString(), location.Name, location.Group.ToString(), location.Position.ToString());
                }
                player.Reply(table.ToString());
                return;
            }
            case "valid" : case "validate" : case "v" : {
                if (args.Length < 2) goto syntax;
                var profile = _config.Profiles.GetValueOrDefault(args.Length > 2 ? args[2] : string.Empty);
                if (profile == null) {
                    player.Reply(GetMsg("Location: Profile Not Found", player.Id));
                    return;
                }
                Vector3 position;
                switch (args[1]) {
                    case "location" : case "l" : if (args.Length < 4 || !int.TryParse(args[3], out var id)) {
                        goto syntax;
                    }
                    var locationIndex = PluginData.Location.FindIndexById(id);
                    if (!locationIndex.HasValue) {
                        player.Reply(GetMsg("Location: Not Found", player.Id));
                        return;
                    }
                    position = _data.Locations[locationIndex.Value].Position;
                    break;
                    case "generated" : case "g" : if (args.Length < 4 || !int.TryParse(args[3], out var index)) {
                        goto syntax;
                    }
                    if (profile.Positions.Count <= index) {
                        player.Reply(GetMsg("Location: Generated Not Found", player.Id));
                        return;
                    }
                    position = profile.Positions[index];
                    break;
                    case "position" : case "p" : if (player.Object is not BasePlayer basePlayer) {
                        player.Reply(GetMsg("Location: Player Only", player.Id));
                        return;
                    }
                    position = basePlayer.ServerPosition;
                    break;
                    default : goto syntax;
                }
                player.Reply(GetMsg("Location: Validation Format", player.Id).Replace("{buildings}", profile.IsValidBuilding(position).ToString()).Replace("{colliders}", profile.IsValidColliders(position).ToString()).Replace("{raycast}", profile.IsValidAbove(position).ToString()).Replace("{biome}", profile.IsValidBiome(position).ToString()).Replace("{topology}", profile.IsValidTopology(position).ToString()).Replace("{splat}", profile.IsValidSplat(position).ToString()));
                return;
            }
            case "debug" : {
                var profile = _config.Profiles.GetValueOrDefault(args.Length > 1 ? string.Join(" ", args.Skip(1)) : string.Empty);
                if (profile == null) {
                    player.Reply(GetMsg("Location: Profile Not Found", player.Id));
                    return;
                }
                player.Reply(GetMsg("Location: Debug", player.Id).Replace("{amount}", profile.Positions.Count.ToString()));
                return;
            }
            case "show" : case "draw" : {
                if (player.Object is not BasePlayer basePlayer) {
                    player.Reply(GetMsg("Location: Player Only", player.Id));
                    return;
                }
                var profile = _config.Profiles.GetValueOrDefault(args.Length > 1 ? args[1] : string.Empty);
                if (profile == null) {
                    player.Reply(GetMsg("Location: Profile Not Found", player.Id));
                    return;
                }
                if (args.Length <= 2 || !float.TryParse(args[2], out var distance) || distance < 0) distance = float.NaN;
                else distance *= distance;
                var playerPosition = basePlayer.transform.position;
                for (var i = 0; i < profile.Positions.Count; i++) {
                    var position = profile.Positions[i];
                    if (!float.IsNaN(distance) && (playerPosition - position).SqrMagnitude2D() >= distance) continue;
                    DDraw.Text(basePlayer, 10f, from : position, text : $"#{i}");
                }
                player.Reply(GetMsg("Location: Shown", player.Id));
                return;
            }
            default : {
                goto syntax;
            }
        }
        syntax : player.Reply(GetMsg("Location: Syntax", player.Id));
        return;
        saveData : SaveData();
    }
    private class CommandLocationData {
        public IPlayer Player;
        public PluginData.Location Location;
        private const int FirstArgumentIndex = 2;
        public void Apply(string[] args) {
            for (var i = FirstArgumentIndex; i + 1 < args.Length; i += 2) {
                switch (args[i]) {
                    case "move" : {
                        var position = ParseVector(args[i + 1].ToLower());
                        if (!position.HasValue) {
                            Player.Reply(GetMsg("Location: Unable To Parse Position", Player.Id));
                            break;
                        }
                        Location.Position = position.Value;
                        break;
                    }
                    case "group" : {
                        var group = -1;
                        if (args[i + 1] != "reset" && !int.TryParse(args[i + 1], out group)) {
                            Player.Reply(GetMsg("Location: Unable To Parse Group", Player.Id));
                            break;
                        }
                        Location.Group = group;
                        break;
                    }
                }
            }
        }
        private Vector3? ParseVector(string argument) {
            var vector = new Vector3();
            if (argument == "here") {
                Player.Position(out vector.x, out vector.y, out vector.z);
            }
            else {
                var coordinates = argument.Split(';');
                if (coordinates.Length != 3 || !float.TryParse(coordinates[0], out vector.x) || !float.TryParse(coordinates[1], out vector.y) || !float.TryParse(coordinates[2], out vector.z)) {
                    return null;
                }
            }
            return vector;
        }
    }
#endregion
#region API
    private Vector3? GetLocation(int id) {
        var locationIndex = PluginData.Location.FindIndexById(id);
        if (!locationIndex.HasValue) return null;
        return _data.Locations[locationIndex.Value].Position;
    }
    private JObject GetGroupLocations(int group) {
        var locations = PluginData.Location.FindByGroup(group);
        return JObject.FromObject(locations);
    }
    private Vector3? GetPregeneratedLocation(string profileName = null) {
        if (profileName == null || !_config.Profiles.TryGetValue(profileName, out var profile)) {
            PrintWarning($"Unknown profile has been retrieved.\n{StackTraceUtility.ExtractStackTrace()}");
            return null;
        }
        return FindPregeneratedPosition(profile);
    }
#endregion
#region Helpers
    private IEnumerator PositionGeneration(Configuration.Profile profile, string name) {
#if DEBUG
        var watch = Stopwatch.StartNew();
#endif
        while (true) {
            if (profile.Positions.Count >= profile.PregeneratedAmount) {
#if DEBUG
                Debug.Log($"{nameof(PositionGeneration)} > {profile.Calls} frames took {profile.CallsTook}ms (AVG: {profile.CallsTook / profile.Calls}ms).Generated (Profile: \"{name}\"): {profile.Positions.Count}+{profile.SkippedGenerations}.");
                profile.Calls = 0;
                profile.CallsTook = 0d;
                profile.SkippedGenerations = 0;
#endif
                yield return new WaitForSeconds(profile.PregeneratedCheck);
                continue;
            }
#if DEBUG
            watch.Start();
#endif
            var attempts = 0;
            var found = 0;
            while (attempts++ < profile.AttemptsPerFrame && found < profile.PositionsPerFrame && profile.Positions.Count < profile.PregeneratedAmount) {
                var position = TryFindPosition(profile);
                if (!position.HasValue) {
#if DEBUG
                    profile.SkippedGenerations++;
#endif
                    continue;
                }
                profile.Positions.Add(position.Value);
                found++;
            }
#if DEBUG
            profile.Calls++;
            profile.CallsTook += watch.Elapsed.TotalMilliseconds;
            watch.Reset();
#endif
            yield return null;
        }
        // ReSharper disable once IteratorNeverReturns
    }
    private Vector3? FindPregeneratedPosition(Configuration.Profile profile) {
        Vector3? position = null;
        for (var i = 0; i < profile.AttemptsPregenerated; i++) {
            if (profile.Positions.Count <= 0) {
#if DEBUG
                Debug.Log($"{nameof(FindPregeneratedPosition)} > There are no pregenerated positions.");
#endif
                return null;
            }
            // index.noice, performance for RemoveAt!
            var index = _random.Next(0, profile.Positions.Count);
            position = profile.Positions[index];
            // If it is a good position, break to return it.if (profile.IsValidPosition(position.Value))
            break;
            // Remove invalid position
            profile.Positions.RemoveAt(index);
            // Reset position value
            position = null;
        }
        return position;
    }
    private Vector3? TryFindPosition(Configuration.Profile profile) {
        var position = new Vector3(GetRandomPosition(), 0, GetRandomPosition());
        var waterInfo = WaterLevel.GetWaterInfo(position, false, false);
        position.y = waterInfo.terrainHeight;
        // Invalid if under the water
        if (waterInfo.isValid) return null;
        // Invalid if has buildings or colliders in the configured range
        if (!profile.IsValidPosition(position)) return null;
        return position;
    }
    private int GetRandomPosition() => _random.Next(-_halfWorldSize, _halfWorldSize);
    private static string GetMsg(string key, string userId = null) => _ins.lang.GetMessage(key, _ins, userId);
    internal static class DDraw {
        public static void Line(BasePlayer player, float duration = 0.5f, Color? color = null, Vector3? from = null, Vector3? to = null) {
            player.SendConsoleCommand("ddraw.line", duration, Format(color), Format(from), Format(to));
        }
        public static void Arrow(BasePlayer player, float duration = 0.5f, Color? color = null, Vector3? from = null, Vector3? to = null, float headSize = 0f) {
            player.SendConsoleCommand("ddraw.arrow", duration, Format(color), Format(from), Format(to), headSize);
        }
        public static void Sphere(BasePlayer player, float duration = 0.5f, Color? color = null, Vector3? from = null, string text = "") {
            player.SendConsoleCommand("ddraw.sphere", duration, Format(color), Format(from), text);
        }
        public static void Text(BasePlayer player, float duration = 0.5f, Color? color = null, Vector3? from = null, string text = "") {
            player.SendConsoleCommand("ddraw.text", duration, Format(color), Format(from), text);
        }
        public static void Box(BasePlayer player, float duration = 0.5f, Color? color = null, Vector3? from = null, float size = 0.1f) {
            player.SendConsoleCommand("ddraw.box", duration, Format(color), Format(from), size);
        }
        private static string Format(Color? color) => ReferenceEquals(color, null) ? string.Empty : $"{color.Value.r},{color.Value.g},{color.Value.b},{color.Value.a}";
        private static string Format(Vector3? pos) => ReferenceEquals(pos, null) ? string.Empty : $"{pos.Value.x} {pos.Value.y} {pos.Value.z}";
    }
#endregion
}

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