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

FREE RoadBradley : 1.7.3 1.7.3

Нет прав для скачивания
C#:
//Reference: 0Harmony
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using UnityEngine;
using Facepunch;
using ProtoBuf;
using System.Text;
using Network;
using Oxide.Core.Libraries.Covalence;
using HarmonyLib;
#if CARBON
using Carbon.Components;
using Carbon.Plugins;
using Carbon;
using Carbon.Base;
using Carbon.Modules;
#else
using Oxide.Ext.CarbonAliases;
using System.Reflection;
#endif
#pragma warning disable IDE0090
namespace Oxide.Plugins {
    [Info("RoadBradley", "ThePitereq", "1.7.3")]
#if CARBON
    public class RoadBradley : CarbonPlugin
#else
    public class RoadBradley : RustPlugin
#endif
    {
        [PluginReference]
        private readonly Plugin Friends, Clans, Economics, ServerRewards, ShoppyStock, PopUpAPI;
        private static readonly ImageDatabaseModule imageDb = BaseModule.GetModule<ImageDatabaseModule>();
        private static HarmonyLib.Harmony harmony;
#if !CARBON
        private static readonly CUI.Handler CuiHandler = new CUI.Handler();
        private static FieldInfo seekerTargets;
        private static FieldInfo targetUpdateTime;
        private static MethodInfo killScientist;
#endif
        private static RoadBradley plugin;
        //ROAD LIST
        private static readonly Dictionary<string, RoadInfo> roads = new Dictionary<string, RoadInfo>();
        //ROAD BRADLEYS
        private static readonly HashSet<BradleyAPC> roadBradleys = new HashSet<BradleyAPC>();
        //PURCHASE COOLDOWNS - NOT LIMITS
        private static readonly Dictionary<ulong, Dictionary<string, int>> purchaseCooldowns = new Dictionary<ulong, Dictionary<string, int>>();
        private static Timer purchaseTimer;
        private static readonly Dictionary<string, DateTime> lastKillTime = new Dictionary<string, DateTime>();
        //UI CACHE
        private static readonly Dictionary<ulong, string> lastSelectedTankUi = new Dictionary<ulong, string>();
        //ITEM ID CACHE FOR BETTER UI PERFORMANCE
        private static readonly Dictionary<string, int> cachedItemIds = new Dictionary<string, int>();
        //CACHED TANK ID FOR CORRECT HEALTH DISPLAY
        private static readonly Dictionary<ulong, ulong> currentTankHealthId = new Dictionary<ulong, ulong>();
        private static readonly Dictionary<ulong, DateTime> timeToOpenCrate = new Dictionary<ulong, DateTime>();
        private static readonly Dictionary<ulong, EditorCache> activeEditors = new Dictionary<ulong, EditorCache>();
        private static readonly Dictionary<string, HashSet<ulong>> tanksWaitingToSpawn = new Dictionary<string, HashSet<ulong>>();
        private class EditorCache {
            public bool allowEdit = false;
            public string currentMode = string.Empty;
            public int currentIndex = 0;
            public string roadName = string.Empty;
            public List<Vector3> cachedCheckpoints;
        }
        private static string mapName;
        private static int mergedTracksCounter = 0;
        private static string coreJson = string.Empty;
        private static string healthJson = string.Empty;
        private static string editorJson = string.Empty;
        private class RoadInfo {
            public RoadType roadType = RoadType.Default;
            public HashSet<string> assignedRandomEvents = new HashSet<string>();
            public bool isPurchasable = false;
            public bool isNowOccupied = false;
            public bool hasBeenMerged = false;
            public string routeName = string.Empty;
            public List<Vector3> checkpoints = new List<Vector3>();
        }
        private static class TankDeathInfo {
            public static Vector3 tankDeathLoc = Vector3.zero;
            public static string tankDeathProfile = string.Empty;
            public static ulong tankDeathOwner = 0;
            public static ulong topDmgUser = 0;
            public static Dictionary<string, int> crateCount = new Dictionary<string, int>();
            public static bool debrisSet = false;
            public static int cratesToSet = 0;
            public static void ResetValues() {
                tankDeathProfile = string.Empty;
                crateCount.Clear();
                debrisSet = false;
                cratesToSet = 0;
            }
        }
        private enum RoadType {
            Default, Edited, Merged, Custom
        }
        private class BradleyCache {
            public int bradleyTimer = -1;
            public ulong bradleyOwner = 0;
        }
        private static readonly Dictionary<string, string> markerPrefabs = new Dictionary<string, string>() {
            {
                "crate", "assets/prefabs/tools/map/cratemarker.prefab"
            }
            , {
                "chinook", "assets/prefabs/tools/map/ch47marker.prefab"
            }
            , {
                "cargoship", "assets/prefabs/tools/map/cargomarker.prefab"
            }
            , {
                "generic", "assets/prefabs/tools/map/genericradiusmarker.prefab"
            }
            , {
                "vending", "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab"
            }
        }
        ;
        private void Init() {
            LoadConfig();
            LoadData();
            harmony = new HarmonyLib.Harmony("com.ThePitereq.RoadBradley");
#if !CARBON
            seekerTargets = typeof(SeekerTarget).GetField("seekerTargets", BindingFlags.NonPublic | BindingFlags.Static);
            targetUpdateTime = typeof(SeekingServerProjectile).GetField("nextTargetUpdateTime", BindingFlags.NonPublic);
            killScientist = typeof(BradleyAPC).GetMethod("KillSpawnedScientists", BindingFlags.NonPublic | BindingFlags.Instance);
#endif
            harmony.Patch(original : AccessTools.Method(typeof(BradleyAPC), nameof(BradleyAPC.DoSimpleAI)), prefix : new HarmonyMethod(typeof(BradleyHarmonyUpdate), nameof(BradleyHarmonyUpdate.AiLock)));
            harmony.Patch(original : AccessTools.Method(typeof(BradleyAPC), nameof(BradleyAPC.DoPhysicsMove)), prefix : new HarmonyMethod(typeof(BradleyHarmonyUpdate), nameof(BradleyHarmonyUpdate.MoveLock)));
            harmony.Patch(original : AccessTools.Method(typeof(BradleyAPC), "TrySpawnScientists"), prefix : new HarmonyMethod(typeof(BradleyHarmonyUpdate), nameof(BradleyHarmonyUpdate.ScientistSpawnLock)));
            if (config.ignoreOwnerships && !config.customTankDeath.fireballLock) Unsubscribe(nameof(CanLootEntity));
            if (!config.enabledBags) Unsubscribe(nameof(CanBeWounded));
            Unsubscribe(nameof(OnWeaponFired));
            Unsubscribe(nameof(OnPlayerAttack));
            Unsubscribe(nameof(OnLoseCondition));
        }
        private void OnServerInitialized() {
            ClearCache();
            plugin = this;
            FindRegularRoads();
            FindMapName();
            TryGenerateDefaultTracks();
            TryGeneratePurchasableTracks();
            PrintRoadInfo();
            LoadMessages();
            AddImages();
            GenerateBaseUi();
            GenerateHealthUi();
            GenerateEditorUi();
            RegisterCommands();
            RegisterPermissions();
            FindRequiredItemIcons();
            CheckForMissingTimedTanks();
        }
        private void Unload() {
            SaveData();
            harmony.UnpatchAll("com.ThePitereq.RoadBradley");
            using CUI cui = new CUI(CuiHandler);
            foreach (var player in BasePlayer.activePlayerList) {
                cui.Destroy("RoadBradleyUI_RoadBradley", player);
                cui.Destroy("RoadBradleyHealthUI_RoadBradleyHealth", player);
                cui.Destroy("RoadBradleyEditorUI_RoadBradleyEditor", player);
            }
            foreach (var bradley in roadBradleys.ToList()) SafeKillBradley(bradley);
        }
        private static void ClearCache() {
            roads.Clear();
            roadBradleys.Clear();
            purchaseCooldowns.Clear();
            lastSelectedTankUi.Clear();
            cachedItemIds.Clear();
            currentTankHealthId.Clear();
            timeToOpenCrate.Clear();
            activeEditors.Clear();
            tanksWaitingToSpawn.Clear();
            TankDeathInfo.ResetValues();
        }
        private struct CachedResource {
            public string shortname;
            public int itemId;
            public ulong skin;
            public string displayName;
            public int minDrops;
            public int maxDrops;
            public Dictionary<string, CachedItem> itemList;
        }
        private struct CachedItem {
            public string shortname;
            public int itemId;
            public ulong skin;
            public string displayName;
            public int amountMin;
            public int amountMax;
            public float chance;
        }
        private string GetDropRates() {
            Dictionary<string, CachedResource> dropRates = new Dictionary<string, CachedResource>();
            StringBuilder sb = Pool.Get<StringBuilder>();
            foreach (var bradDrop in config.lootPresets) {
                int itemId = ItemManager.itemDictionaryByName["box.wooden.large"].itemid;
                string key = sb.Clear().Append("box.wooden.large_").Append(bradDrop.Key).ToString();
                dropRates.Add(key, new CachedResource() {
                    shortname = "box.wooden.large", skin = 0, itemId = itemId, displayName = "Bradley Drop", minDrops = bradDrop.Value.minItems, maxDrops = bradDrop.Value.maxItems, itemList = new Dictionary<string, CachedItem>()
                }
                );
                int summedChance = 0;
                foreach (var drop in bradDrop.Value.lootTable) summedChance += drop.chance;
                foreach (var drop in bradDrop.Value.lootTable) {
                    ItemDefinition def = ItemManager.FindItemDefinition(drop.shortname);
                    itemId = def == null ? 0 : def.itemid;
                    float chance = 100f / summedChance * drop.chance;
                    if (drop.alwaysInclude > 0 && drop.chance == 0) chance = drop.alwaysInclude / bradDrop.Value.maxItems;
                    dropRates[key].itemList.Add(sb.Clear().Append(drop.shortname).Append('_').Append(drop.skin).ToString(), new CachedItem() {
                        amountMin = drop.amount - drop.amountRandomize, amountMax = drop.amount + drop.amountRandomize, chance = chance, displayName = drop.displayName, itemId = itemId, shortname = drop.shortname, skin = drop.skin
                    }
                    );
                }
            }
            Pool.FreeUnmanaged(ref sb);
            return JsonConvert.SerializeObject(dropRates);
        }
        private static void AddImages() {
            List<string> urls = Pool.Get<List<string>>();
            foreach (var purchase in config.purchases.Values) foreach (var req in purchase.requiredItems) if (!string.IsNullOrEmpty(req.iconUrl)) urls.Add(req.iconUrl);
            imageDb.QueueBatch(false, urls);
            Pool.FreeUnmanaged(ref urls);
        }
        private void OnNewSave() {
            bagData.bags.Clear();
            limitData.limits.Clear();
            SaveData();
        }
        private static void FindMapName() {
            mapName = string.IsNullOrEmpty(ConVar.Server.levelurl) ? "default" : ConVar.Server.levelurl.Split('/').Last();
            if (ConVar.Server.level == "HapisIsland") mapName = ConVar.Server.level;
            else if (!config.mapTracks.ContainsKey(mapName)) mapName = "default";
        }
        private static void FindRequiredItemIcons() {
            foreach (var purchase in config.purchases.Values) foreach (var item in purchase.requiredItems) {
                ItemDefinition def = ItemManager.FindItemDefinition(item.shortname);
                if (def == null) continue;
                cachedItemIds.TryAdd(item.shortname, def.itemid);
            }
        }
        private static void FindRegularRoads() {
            roads.Clear();
            int counter = -1;
            bool foundFirstRing = false;
            foreach (var road in TerrainMeta.Path.Roads) {
                counter++;
                string counterString = counter.ToString();
                roads.TryAdd(counterString, new RoadInfo());
                roads[counterString].checkpoints = new List<Vector3>(road.Path.Points);
                if (config.asphaltOnly && road.Width <= 5) continue;
                string editedKey = $"{counter}_Edited";
                roads.Add(editedKey, new RoadInfo() {
                    roadType = RoadType.Edited, isPurchasable = true
                }
                );
                if (!foundFirstRing && config.removeRingRoad && road.Path.GetStartPoint() == road.Path.GetEndPoint()) {
                    roads.Remove(editedKey);
                    Debug.LogWarning($"[RoadBradley WARNING] Route '{editedKey}' has been removed from the pool as it's ring road for Traveling Vendor!");
                    foundFirstRing = true;
                    continue;
                }
                Dictionary<Vector3, string> obstacles = config.checkObstacles ? FindObstaclePrefabs() : null;
                int checkpointCount = road.Path.Points.Length;
                bool isLooped = (checkpointCount > 0 && Vector3.Distance(road.Path.Points[0], road.Path.Points[checkpointCount - 1]) < 20f);
                int checkpointCounter = 0;
                bool failedCheck = false;
                foreach (var checkpoint in road.Path.Points) {
                    checkpointCounter++;
                    if (!isLooped && (checkpointCounter <= config.removeCheckpointAmount || checkpointCounter >= checkpointCount - config.removeCheckpointAmount)) continue;
                    if (config.checkObstacles) {
                        float checkWidth = road.Width / 2f;
                        foreach (var obstacle in obstacles) {
                            if (Vector3.Distance(checkpoint, obstacle.Key) < checkWidth) {
                                if (config.removeObstacleRoads) {
                                    Debug.LogWarning($"[RoadBradley WARNING] There is an obstacle '{obstacle.Value}' on the road '{counter}' at '{obstacle.Key}'.Removing road from the road pool!");
                                    roads.Remove(editedKey);
                                    failedCheck = true;
                                    break;
                                }
                                else Debug.LogWarning($"[RoadBradley WARNING] There is an obstacle '{obstacle.Value}' on the road '{counter}' at '{obstacle.Key}'.");
                            }
                        }
                        if (failedCheck) break;
                    }
                    Vector3 checkpointFixed = checkpoint;
                    checkpointFixed.y += 2f;
                    RaycastHit hit;
                    if (Physics.Raycast(checkpointFixed, Vector3.down, out hit, 4f, Layers.World)) checkpointFixed = hit.point;
                    else checkpointFixed.y = TerrainMeta.HeightMap.GetHeight(checkpointFixed) + 0.25f;
                    roads[editedKey].checkpoints.Add(checkpointFixed);
                }
                if (failedCheck) continue;
                checkpointCount = roads[editedKey].checkpoints.Count;
                if (checkpointCount < config.minRouteLength) {
                    roads.Remove(editedKey);
                    Debug.LogWarning($"[RoadBradley WARNING] Route '{editedKey}' is too small for a bradley! It have less than {config.minRouteLength} checkpoints.Cannot be used.");
                    continue;
                }
                if (!isLooped) {
                    checkpointCount -= 2;
                    while (checkpointCount > 0) {
                        roads[editedKey].checkpoints.Add(roads[editedKey].checkpoints[checkpointCount]);
                        checkpointCount--;
                    }
                }
            }
            foreach (var road in roadData.roads) {
                roads.Add(road.Key, new RoadInfo() {
                    roadType = RoadType.Custom, checkpoints = road.Value
                }
                );
            }
        }
        private static Dictionary<Vector3, string> FindObstaclePrefabs() {
            Dictionary<Vector3, string> obstacles = new Dictionary<Vector3, string>();
            foreach (PrefabData entry in World.Serialization.world.prefabs) {
                if (TerrainMeta.TopologyMap.GetTopology(entry.position, TerrainTopology.ROAD) && !obstacles.ContainsKey(entry.position)) {
                    string prefabName = StringPool.Get(entry.id).ToLower();
                    bool shouldAdd = true;
                    foreach (var whitelist in config.obstacleWhitelist) {
                        if (prefabName.Contains(whitelist.ToLower())) {
                            shouldAdd = false;
                            break;
                        }
                    }
                    if (shouldAdd) obstacles.Add(entry.position, prefabName);
                }
            }
            return obstacles;
        }
        private void RegisterCommands() {
#if CARBON
            cmd.AddConsoleCommand(Community.Protect("RoadBradleyUI"), this, nameof(RoadBradleyConsoleCommand), @protected : true);
            cmd.AddConsoleCommand(Community.Protect("RoadBradleyEditorUI"), this, nameof(RoadBradleyEditorConsoleCommand), @protected : true);
            foreach (var command in config.commands) cmd.AddCovalenceCommand(command, this, nameof(RoadBradleyCommand));
            cmd.AddCovalenceCommand("tankroute", this, nameof(RoadBradleyRouteCommand));
#else
            cmd.AddConsoleCommand("RoadBradleyUI", this, nameof(RoadBradleyConsoleCommand));
            cmd.AddConsoleCommand("RoadBradleyEditorUI", this, nameof(RoadBradleyEditorConsoleCommand));
            foreach (var command in config.commands) cmd.AddChatCommand(command, this, nameof(RoadBradleyCommand));
            cmd.AddChatCommand("tankroute", this, nameof(RoadBradleyRouteCommand));
#endif
            AddCovalenceCommand("spawnrbtank", nameof(SpawnTankCommand));
            AddCovalenceCommand("tankcount", nameof(TankCountCommand));
        }
        private void RegisterPermissions() {
            permission.RegisterPermission("roadbradley.admin", this);
            permission.RegisterPermission("roadbradley.bypass", this);
            foreach (var purchase in config.purchases.Values) {
                foreach (var perm in purchase.purchasePermissionLimits.Keys) if (!permission.PermissionExists(perm, this)) permission.RegisterPermission(perm, this);
                if (purchase.permission != "" && !permission.PermissionExists(purchase.permission, this)) permission.RegisterPermission(purchase.permission, this);
            }
        }
        private void RoadBradleyConsoleCommand(ConsoleSystem.Arg arg) {
            BasePlayer player = arg.Player();
            using CUI cui = new CUI(CuiHandler);
            switch (arg.Args[0]) {
                case "close" : cui.Destroy("RoadBradleyUI_RoadBradley", player);
                break;
                case "profile" : CUI.Handler.UpdatePool elements = cui.UpdatePool();
                StringBuilder sb = Pool.Get<StringBuilder>();
                string oldProfile = lastSelectedTankUi[player.userID];
                lastSelectedTankUi[player.userID] = arg.Args[1];
                UpdateDisplayedTankUi(player, sb, cui, elements);
                cui.Destroy("RoadBradleyUI_ItemSection", player);
                UpdateLeftButton(player, oldProfile, lastSelectedTankUi[player.userID], sb, cui, elements);
                elements.Send(player);
                Pool.FreeUnmanaged(ref sb);
                break;
                case "spawn" : cui.Destroy("RoadBradleyUI_RoadBradley", player);
                PurchaseTank(player, lastSelectedTankUi[player.userID]);
                break;
                case "hint" : PopUpAPI?.Call("ShowPopUp", player, config.popUpProfile, Lang(arg.Args[1], player.UserIDString));
                break;
                case "reqItem" : string profile = arg.Args[1];
                string slotIdentifier = arg.Args[2];
                PopUpAPI?.Call("ShowPopUp", player, config.popUpProfile, Lang($"ReqItemHint_{profile}_{slotIdentifier}", player.UserIDString));
                break;
            }
        }
        private void RoadBradleyEditorConsoleCommand(ConsoleSystem.Arg arg) {
            BasePlayer player = arg.Player();
            if (!permission.UserHasPermission(player.UserIDString, "roadbradley.admin")) return;
            using CUI cui = new CUI(CuiHandler);
            switch (arg.Args[0]) {
                case "close" : SendReply(player, Lang("EditorClosedNotSaved", player.UserIDString));
                cui.Destroy("RoadBradleyEditorUI_RoadBradleyEditor", player);
                activeEditors.Remove(player.userID);
                if (activeEditors.Count == 0) {
                    Unsubscribe(nameof(OnWeaponFired));
                    Unsubscribe(nameof(OnPlayerAttack));
                    Unsubscribe(nameof(OnLoseCondition));
                }
                break;
                case "showCheckpoints" : DisplayRoute(player);
                break;
                case "showHelp" : SendReply(player, Lang("EditorHint", player.UserIDString));
                break;
                case "saveExit" : SaveRouteAndExit(player);
                break;
            }
        }
        private void RoadBradleyCommand(BasePlayer player) => OpenRoadBradleyUI(player);
        private void RoadBradleyRouteCommand(BasePlayer player, string command, string[] args) {
            if (!permission.UserHasPermission(player.UserIDString, "roadbradley.admin")) {
                SendReply(player, Lang("NoPermission", player.UserIDString));
                return;
            }
            if (args.Length == 0) {
                SendReply(player, Lang("RouteHelpV4", player.UserIDString));
                return;
            }
            string type = args[0].ToLower();
            switch (type) {
                case "new" : if (args.Length == 1) {
                    SendReply(player, Lang("InvalidNewRoadSyntax", player.UserIDString));
                    return;
                }
                TryCreateRoute(player, args[1]);
                break;
                case "default" : case "edited" : case "merged" : case "custom" : ListRoutes(player, type);
                break;
                case "clone" : if (args.Length == 1) {
                    SendReply(player, Lang("InvalidRouteNameSyntax", player.UserIDString));
                    return;
                }
                string routeName = args[1];
                CloneRoute(player, routeName);
                break;
                default : TryEditRoute(player, args[0]);
                break;
            }
        }
        private void SpawnTankCommand(IPlayer user, string command, string[] args) {
            if (!user.IsAdmin && !permission.UserHasPermission(user.Id, "roadbradley.admin")) {
                user.Reply(Lang("NoPermission", user.Id));
                return;
            }
            if (args.Length != 2) {
                user.Reply(Lang("SpawnHelp", user.Id));
                return;
            }
            string profile = args[0];
            string routeOrPlayer = args[1];
            if (!config.profiles.ContainsKey(profile)) {
                user.Reply(Lang("ProfileNotFound", user.Id, profile));
                return;
            }
            ulong userId;
            if (ulong.TryParse(routeOrPlayer, out userId) && userId > 1000000000) {
                BasePlayer spawnUser = BasePlayer.FindByID(userId);
                if (spawnUser == null) {
                    user.Reply(Lang("PlayerOffline", user.Id, userId));
                    return;
                }
                SpawnRoadBradley(profile, spawnUser);
                user.Reply(Lang("TankSpawnedUser", user.Id, userId));
                Puts($"Spawned tank with profile {profile} for {spawnUser.displayName} ({spawnUser.UserIDString}).");
            }
            else {
                if (!config.spawns.ContainsKey(routeOrPlayer)) {
                    user.Reply(Lang("RouteNotFound", user.Id, routeOrPlayer));
                    return;
                }
                if (!config.mapTracks[mapName].Contains(routeOrPlayer)) {
                    user.Reply(Lang("RouteNotAddedToConfig", user.Id, routeOrPlayer, mapName));
                    return;
                }
                SpawnRoadBradley(profile, null, routeOrPlayer);
                user.Reply(Lang("TankSpawned", user.Id));
                Puts($"Spawned tank with profile {profile} on route {routeOrPlayer}.");
            }
        }
        private void TankCountCommand(IPlayer user) {
            if (!user.IsAdmin && !permission.UserHasPermission(user.Id, "roadbradley.admin")) {
                user.Reply(Lang("NoPermission", user.Id));
                return;
            }
            StringBuilder sb = Pool.Get<StringBuilder>();
            sb.Clear().Append("Road Bradleys Active: ").Append(roadBradleys.Count).AppendLine().Append("Random Spawned: ").Append(roadBradleys.Count(x => x.GetComponent<RoadBradleyController>().ownerId == 0)).AppendLine().Append("Player Owned: ").Append(roadBradleys.Count(x => x.GetComponent<RoadBradleyController>().ownerId != 0));
            user.Reply(sb.ToString());
            Pool.FreeUnmanaged(ref sb);
        }
        private static readonly Dictionary<string, RoadType> roadTypeConvert = new Dictionary<string, RoadType>() {
            {
                "default", RoadType.Default
            }
            , {
                "edited", RoadType.Edited
            }
            , {
                "merged", RoadType.Merged
            }
            , {
                "custom", RoadType.Custom
            }
        }
        ;
        private void ListRoutes(BasePlayer player, string type) {
            StringBuilder sb = Pool.Get<StringBuilder>();
            sb.Clear().AppendLine(Lang("AvailableRoads", player.UserIDString, type));
            int counter = 0;
            foreach (var road in roads) if (road.Value.roadType == roadTypeConvert[type]) {
                counter++;
                sb.AppendLine(Lang("RoadListed", player.UserIDString, road.Key));
            }
            if (counter == 0) sb.AppendLine(Lang("NoRoadFound", player.UserIDString));
            SendReply(player, sb.ToString());
            Pool.FreeUnmanaged(ref sb);
        }
        private void TryEditRoute(BasePlayer player, string roadKey) {
            if (!roads.ContainsKey(roadKey)) {
                SendReply(player, Lang("NoRoadNamedFound", player.UserIDString, roadKey));
                return;
            }
            StartRouteEdit(player, roadKey);
        }
        private void TryCreateRoute(BasePlayer player, string roadName) {
            if (roads.ContainsKey(roadName)) {
                SendReply(player, Lang("RoadAlreadyCreated", player.UserIDString, roadName));
                return;
            }
            roads.Add(roadName, new RoadInfo() {
                roadType = RoadType.Custom
            }
            );
            StartRouteEdit(player, roadName);
        }
        private void CloneRoute(BasePlayer player, string roadName) {
            if (!roads.ContainsKey(roadName)) {
                SendReply(player, Lang("RoadNotExist", player.UserIDString, roadName));
                return;
            }
            string fixedName = $"{roadName}_Clone";
            if (roads.ContainsKey(fixedName)) {
                SendReply(player, Lang("AlreadyCloned", player.UserIDString));
                return;
            }
            roads.Add(fixedName, new RoadInfo() {
                roadType = RoadType.Custom, checkpoints = roads[roadName].checkpoints
            }
            );
            StartRouteEdit(player, fixedName);
        }
        private void StartRouteEdit(BasePlayer player, string roadName) {
            SendJson(player, editorJson);
            GiveEditorTool(player);
            if (activeEditors.Count == 0) {
                Subscribe(nameof(OnWeaponFired));
                Subscribe(nameof(OnPlayerAttack));
                Subscribe(nameof(OnLoseCondition));
            }
            activeEditors.Add(player.userID, new EditorCache() {
                allowEdit = roads[roadName].roadType == RoadType.Custom, cachedCheckpoints = new List<Vector3>(roads[roadName].checkpoints), roadName = roadName,
            }
            );
            UpdateRouteEditor(player);
            DisplayRoute(player);
            if (roads[roadName].roadType != RoadType.Custom) SendReply(player, Lang("RoadPreviewOnly", player.UserIDString));
            SendReply(player, Lang("RouteLoaded", player.UserIDString, roadName));
            SendReply(player, Lang("EditorHint", player.UserIDString));
        }
        private void SaveRouteAndExit(BasePlayer player) {
            if (roads[activeEditors[player.userID].roadName].roadType == RoadType.Custom) {
                roadData.roads.TryAdd(activeEditors[player.userID].roadName, new List<Vector3>(activeEditors[player.userID].cachedCheckpoints));
                SendReply(player, Lang("RouteSaved", player.UserIDString, activeEditors[player.userID].roadName));
                SaveData();
            }
            else SendReply(player, Lang("EditorClosedNotSaved", player.UserIDString));
            using CUI cui = new CUI(CuiHandler);
            cui.Destroy("RoadBradleyEditorUI_RoadBradleyEditor", player);
            activeEditors.Remove(player.userID);
            if (activeEditors.Count == 0) {
                Unsubscribe(nameof(OnWeaponFired));
                Unsubscribe(nameof(OnPlayerAttack));
                Unsubscribe(nameof(OnLoseCondition));
            }
        }
        private void OnPlayerConnected(BasePlayer player) => CheckForMissingTimedTanks();
        private static void GiveEditorTool(BasePlayer player) {
            Item weapon = ItemManager.CreateByName("rifle.semiauto", 1, 0);
            weapon.name = "Road Bradley Editor Tool";
            if (weapon.MoveToContainer(player.inventory.containerBelt)) {
                BaseProjectile weaponProj = weapon.GetHeldEntity() as BaseProjectile;
                if (weaponProj != null) {
                    weaponProj.primaryMagazine.contents = weaponProj.primaryMagazine.capacity;
                    weaponProj.SendNetworkUpdateImmediate();
                }
                Item silencer = ItemManager.CreateByName("weapon.mod.silencer");
                silencer.MoveToContainer(weapon.contents);
            }
        }
        private void OnWeaponFired(BaseProjectile projectile, BasePlayer player) {
            if (!activeEditors.ContainsKey(player.userID)) return;
            projectile.primaryMagazine.contents = projectile.primaryMagazine.capacity;
            projectile.SendNetworkUpdateImmediate();
        }
        private void OnLoseCondition(Item item, ref float amount) {
            BasePlayer player = item.GetOwnerPlayer();
            if (player == null || !activeEditors.ContainsKey(player.userID)) return;
            amount = 0;
        }
        private void OnPlayerAttack(BasePlayer attacker, HitInfo info) {
            if (info == null || !activeEditors.ContainsKey(attacker.userID)) return;
            EditorCache controller = activeEditors[attacker.userID];
            if (roads[controller.roadName].roadType != RoadType.Custom) return;
            Vector3 hitPosition = info.HitPositionWorld + new Vector3(0, config.editorHeightOffset, 0);
            if (controller.currentMode == "") {
                if (attacker.serverInput.IsDown(BUTTON.SPRINT)) {
                    int counter = 0;
                    foreach (var checkpoint in controller.cachedCheckpoints) {
                        if (Vector3.Distance(hitPosition, checkpoint) < 2f) {
                            controller.currentMode = "Insert";
                            controller.currentIndex = counter;
                            SendReply(attacker, Lang("InsertInfo", attacker.UserIDString, counter));
                            return;
                        }
                        counter++;
                    }
                }
                else if (attacker.serverInput.IsDown(BUTTON.DUCK)) {
                    int counter = 0;
                    foreach (var checkpoint in controller.cachedCheckpoints) {
                        if (Vector3.Distance(hitPosition, checkpoint) < 2f) {
                            controller.cachedCheckpoints.RemoveAt(counter);
                            UpdateCheckpointCount(attacker);
                            SendReply(attacker, Lang("CheckpointRemove", attacker.UserIDString, counter));
                            DisplayRoute(attacker, 2);
                            return;
                        }
                        counter++;
                    }
                }
                else {
                    int counter = 0;
                    foreach (var checkpoint in controller.cachedCheckpoints) {
                        if (Vector3.Distance(hitPosition, checkpoint) < 2f) {
                            controller.currentMode = "Update";
                            controller.currentIndex = counter;
                            SendReply(attacker, Lang("UpdateInfo", attacker.UserIDString, counter));
                            return;
                        }
                        counter++;
                    }
                    controller.cachedCheckpoints.Add(hitPosition);
                    UpdateCheckpointCount(attacker);
                    SendReply(attacker, Lang("CheckpointAdd", attacker.UserIDString));
                    DisplayRoute(attacker, 2);
                }
            }
            else if (controller.currentMode == "Insert") {
                controller.cachedCheckpoints.Insert(controller.currentIndex + 1, hitPosition);
                controller.currentMode = "";
                UpdateCheckpointCount(attacker);
                SendReply(attacker, Lang("CheckpointInsert", attacker.UserIDString));
                DisplayRoute(attacker, 2);
            }
            else if (controller.currentMode == "Update") {
                controller.cachedCheckpoints[controller.currentIndex] = hitPosition;
                controller.currentMode = "";
                UpdateCheckpointCount(attacker);
                SendReply(attacker, Lang("CheckpointUpdate", attacker.UserIDString));
                DisplayRoute(attacker, 2);
            }
        }
        private static void DisplayRoute(BasePlayer player, float time = 15f) {
            if (!activeEditors.ContainsKey(player.userID) || activeEditors[player.userID].cachedCheckpoints.Count == 0) return;
            if (player.net.connection.authLevel == 0) {
                player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
                player.SendNetworkUpdateImmediate();
            }
            Vector3 firstCheckPoint = activeEditors[player.userID].cachedCheckpoints[0];
            int counter = 0;
            foreach (var checkpoint in activeEditors[player.userID].cachedCheckpoints) {
                if (firstCheckPoint != checkpoint) {
                    player.SendConsoleCommand("ddraw.line", time, Color.blue, firstCheckPoint, checkpoint);
                    firstCheckPoint = checkpoint;
                }
                player.SendConsoleCommand("ddraw.text", time, Color.green, checkpoint, counter);
                counter++;
            }
            if (player.net.connection.authLevel == 0) {
                player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
                player.SendNetworkUpdateImmediate();
            }
        }
        private object CanBradleyApcTarget(BradleyAPC apc, BasePlayer player) {
            if (!roadBradleys.Contains(apc)) return null;
            if (player.IsNpc) return config.targetNpc ? true : null;
            RoadBradleyController controller = apc.GetComponent<RoadBradleyController>();
            if (config.enableTargetHook) {
                object call = Interface.Call("CanRoadBradleyTarget", apc, player);
                if (call is bool) return call;
            }
            if (controller.targets.Contains(player)) {
                currentTankHealthId[player.userID] = apc.net.ID.Value;
                return null;
            }
            else {
                ProfileConfig configValue = config.profiles[controller.profile];
                if (!config.targetSleepers && player.IsSleeping()) return false;
                Item activeItem;
                bool validTarget = Vector3.Distance(player.transform.position, apc.transform.position) <= configValue.maxTargetDistance;
                if (validTarget) {
                    if (controller.ownerId != 0 && config.purchases[controller.profile].ownerTarget) {
                        if (ValidatePerm(controller.ownerId, player)) {
                            controller.AddTarget(player);
                            return null;
                        }
                        else return false;
                    }
                    else {
                        controller.AddTarget(player);
                        return null;
                    }
                }
                activeItem = player.GetActiveItem();
                validTarget = activeItem != null && configValue.targetItems.Contains(activeItem.info.shortname);
                if (validTarget) {
                    if (controller.ownerId != 0 && config.purchases[controller.profile].ownerTarget) {
                        if (ValidatePerm(controller.ownerId, player)) {
                            controller.AddTarget(player);
                            return null;
                        }
                        else return false;
                    }
                    else {
                        controller.AddTarget(player);
                        return null;
                    }
                }
                else return false;
            }
        }
        private static readonly List<string> validNullDamageEntities = new List<string>() {
            "cupboard.tool.deployed", "bed_deployed", "sleepingbag_leather_deployed", "beachtowel.deployed"
        }
        ;
        private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
            if (entity.ShortPrefabName == "bradleyapc") {
                BradleyAPC apc = entity as BradleyAPC;
                if (!roadBradleys.Contains(apc)) return null;
                if (info.Initiator != null && info.Initiator == apc) return config.hookReturnValue;
                BasePlayer player = info?.InitiatorPlayer;
                if (player == null) return null;
                RoadBradleyController controller = apc.GetComponent<RoadBradleyController>();
                if (controller == null) return null;
                if (controller.targets.Contains(player)) {
                    controller.damageDealt.TryAdd(player.userID, 0);
                    controller.damageDealt[player.userID] += info.damageTypes.Total();
                    currentTankHealthId[player.userID] = apc.net.ID.Value;
                    controller.CheckMLRSSpawn();
                    return null;
                }
                if (controller.ownerId != 0 && config.purchases[controller.profile].ownerDamage) {
                    if (ValidatePerm(controller.ownerId, player)) {
                        controller.AddTarget(player);
                        controller.damageDealt.TryAdd(player.userID, 0);
                        controller.damageDealt[player.userID] += info.damageTypes.Total();
                        controller.CheckMLRSSpawn();
                        return null;
                    }
                    else {
                        SendReply(player, Lang("CannotDamage", player.UserIDString));
                        return config.hookReturnValue;
                    }
                }
                else {
                    controller.AddTarget(player);
                    controller.damageDealt.TryAdd(player.userID, 0);
                    controller.damageDealt[player.userID] += info.damageTypes.Total();
                    controller.CheckMLRSSpawn();
                    return null;
                }
            }
            if (entity.OwnerID == 0) return null;
            if (info.Initiator == null || info.Initiator.ShortPrefabName != "bradleyapc") return null;
            BradleyAPC apc2 = info.Initiator as BradleyAPC;
            if (config.disableUnownedDamage && apc2.OwnerID == 0 && entity is DecayEntity) return config.hookReturnValue;
            if (config.disableTcBagDamage && validNullDamageEntities.Contains(entity.ShortPrefabName)) return config.hookReturnValue;
            if (info.WeaponPrefab == null) return null;
            if (config.callerDamage && info.WeaponPrefab.ShortPrefabName == "maincannonshell") {
                RoadBradleyController controller = apc2.GetComponent<RoadBradleyController>();
                if (controller == null || controller.ownerId == 0 || controller.ownerId == entity.OwnerID) return null;
                if (config.callerTeamCheck) {
                    RelationshipManager.PlayerTeam team = RelationshipManager.ServerInstance.FindPlayersTeam(entity.OwnerID);
                    if (team != null && team.members.Contains(controller.ownerId)) return null;
                }
                if (controller.targets.Any(x => x.userID == entity.OwnerID)) return null;
                controller.damageAuths++;
                if (controller.damageAuths == config.damageAuths) controller.tankRemainingTime = 1;
                if (controller.owner != null) {
                    if (controller.damageAuths % config.damageAuthsRemind == 0 && controller.damageAuths < config.damageAuths) SendReply(controller.owner, Lang("FightUnauthorizedInfo", controller.owner.UserIDString, controller.damageAuths, config.damageAuths));
                    if (controller.damageAuths == config.damageAuths) SendReply(controller.owner, Lang("FightLostUnauthorized", controller.owner.UserIDString, config.damageAuths));
                }
                return config.hookReturnValue;
            }
            else if (info.WeaponPrefab.ShortPrefabName == "rocket_mlrs") {
                //TODO MAYBE ADD ANOTHER DICTIONARY FOR MLRS AND CHECK FOR THEIR NET.ID IF THEY BELONG TO TANK IF TANK IS PRESENT JUST CONTINUE AND IF NOT JUST CANCEL BEHAVIOUR
                if (apc2.IsDestroyed) return config.hookReturnValue;
                RoadBradleyController controller = apc2.GetComponent<RoadBradleyController>();
                if (controller == null) return null;
                bool isTankOwned = controller.ownerId != 0;
                if (!isTankOwned) return null;
                DecayEntity hitEntity = entity as DecayEntity;
                if (hitEntity == null) {
                    BasePlayer hitPlayer = entity as BasePlayer;
                    if (hitPlayer != null) {
                        if (!isTankOwned) return null;
                        if (!controller.targets.Contains(hitPlayer)) return config.hookReturnValue;
                        return null;
                    }
                    else return null;
                }
                if (hitEntity.OwnerID == controller.owner.userID) return null;
                BuildingPrivlidge tc = hitEntity.GetBuildingPrivilege();
                if (tc == null) return null;
                if (!tc.IsAuthed(controller.ownerId)) return config.hookReturnValue;
            }
            return null;
        }
        private object CanBeWounded(BasePlayer player, HitInfo info) {
            if (info == null) return null;
            return OnKilledByBradley(player, info, true);
        }
        private object OnPlayerDeath(BasePlayer player, HitInfo info) {
            if (player is NPCPlayer) return null;
            foreach (var tank in roadBradleys) {
                RoadBradleyController controller = tank?.GetComponent<RoadBradleyController>();
                controller.targets.Remove(player);
            }
            if (config.enabledBags && info != null) return OnKilledByBradley(player, info);
            else return null;
        }
        private void OnPlayerDisconnected(BasePlayer player) {
            foreach (var tank in roadBradleys) {
                RoadBradleyController controller = tank?.GetComponent<RoadBradleyController>();
                controller.RemoveTarget(player);
            }
        }
        private object CanDropActiveItem(BasePlayer player) {
            if (player is NPCPlayer) return null;
            if (player.lastAttacker != null && player.lastAttacker is BradleyAPC) return false;
            return null;
        }
        private object CanLootEntity(BasePlayer player, LockedByEntCrate crate) {
            if (!config.ignoreOwnerships) {
                if (crate.OwnerID != 0 && crate.HasFlag(BaseEntity.Flags.Unused23)) {
                    if (!ValidatePerm(crate.OwnerID, player)) {
                        SendReply(player, Lang("CannotLoot", player.UserIDString));
                        return false;
                    }
                }
            }
            if (!config.customTankDeath.fireballLock && timeToOpenCrate.ContainsKey(crate.net.ID.Value)) {
                if (DateTime.Now < timeToOpenCrate[crate.net.ID.Value]) {
                    int seconds = (int)Math.Ceiling((timeToOpenCrate[crate.net.ID.Value] - DateTime.Now).TotalSeconds);
                    Effect.server.Run("assets/prefabs/locks/keypad/effects/lock.code.denied.prefab", crate.transform.position);
                    SendReply(player, Lang("CannotOpenYet", player.UserIDString, seconds));
                    return false;
                }
                timeToOpenCrate.Remove(crate.net.ID.Value);
            }
            return null;
        }
        private void SetupCrate(LockedByEntCrate crate) {
            if (TankDeathInfo.cratesToSet <= 0) return;
            if (Vector3.Distance(crate.transform.position, TankDeathInfo.tankDeathLoc) > 10) return;
            crate.SetFlag(BaseEntity.Flags.Unused23, true);
            crate.inventory.capacity = 48;
            crate.panelName = "generic_resizable";
            if (TankDeathInfo.tankDeathOwner != 0 && config.purchases[TankDeathInfo.tankDeathProfile].ownerLoot) crate.OwnerID = TankDeathInfo.tankDeathOwner;
            if (config.limitToTopDamage && crate.OwnerID == 0 && TankDeathInfo.topDmgUser != 0) crate.OwnerID = TankDeathInfo.topDmgUser;
            if (config.profiles[TankDeathInfo.tankDeathProfile].lootPreset != "") {
                foreach (var item in crate.inventory.
// ... [Code preview truncated due to length. Full source in attached file] ...

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