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

FREE Basements : 1.0.4 1.0.4

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Oxide.Game.Rust.Cui;
using Facepunch.Extend;
using Newtonsoft.Json.Converters;
using UnityEngine;
using Oxide.Core.Libraries.Covalence;
using Oxide.Plugins.BasementMethods;
using Oxide.Core;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core.Plugins;
using System;
namespace Oxide.Plugins {
    [Info("Basements", "mr01sam", "1.0.4")]
    [Description("Allows players to build basements underneath their base.")]
    public partial class Basements : CovalencePlugin {
        public static Basements INSTANCE;
        public const string PermissionBuild = "basements.build";
        public const string PermissionAdmin = "basements.admin";
        public const string PermissionFree = "basements.free";
        public bool allowDestroy = false;
        public bool newSaveLoaded = false;
        public static bool debugging = false;
        public static void DEBUG(string message) {
            if (debugging) {
                INSTANCE?.Puts(message);
            }
        }
        void Init() {
            INSTANCE = this;
            UnsubscribeAll(nameof(OnLootEntity), nameof(OnLootEntityEnd), nameof(OnActiveItemChanged), nameof(OnStructureRepair), nameof(OnEntitySpawned), nameof(OnEntityKill), nameof(OnEntityDeath), nameof(OnStructureDemolish), nameof(OnStructureRotate), nameof(OnStructureUpgrade), nameof(OnEntityTakeDamage), nameof(CanDemolish), nameof(OnDoorOpened), nameof(CanBuild), nameof(OnBuildingSplit), nameof(OnBuildingMerge), nameof(OnPlayerViolation), nameof(OnHammerHit), nameof(CanLock), nameof(CanUnlock), nameof(CanChangeCode) );
        }
        void OnServerInitialized() {
            // Prevent auto suicide if underground
            ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), "antihack.terrain_protection 0");
            if (!permission.PermissionExists(PermissionFree)) {
                permission.RegisterPermission(PermissionFree, this);
            }
            if (!newSaveLoaded) {
                LoadData();
            }
            SubscribeAll();
            if (!config.SyncLocksOnBasementEntrances) {
                Unsubscribe(nameof(CanLock));
                Unsubscribe(nameof(CanUnlock));
                Unsubscribe(nameof(CanChangeCode));
            }
        }
        void Unload() {
            SaveData();
            foreach (var basePlayer in BasePlayer.activePlayerList) {
                if (basePlayer == null) {
                    continue;
                }
                DestroyToolcupboardButton(basePlayer);
                DestroyPlacementBanner(basePlayer);
                DestroyInsufficientResources(basePlayer);
            }
        }
        void OnNewSave(string strFilename) {
            Puts("Map wipe detected, current data files will be cleared upon save.");
            newSaveLoaded = true;
        }
        void OnServerSave() {
            SaveData();
        }
        void SaveData() {
            Interface.Oxide.DataFileSystem.WriteObject($"Basements/BasementEntities", BasementEntities);
            Interface.Oxide.DataFileSystem.WriteObject($"Basements/BasementEntrances", BasementEntrances);
            Interface.Oxide.DataFileSystem.WriteObject($"Basements/BasementStructures", BasementStructures);
        }
        void LoadData() {
            BasementEntities = Interface.Oxide.DataFileSystem.ReadObject<HashSet<ulong>>($"Basements/BasementEntities") ?? new HashSet<ulong>();
            BasementEntrances = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, BasementEntrance>>($"Basements/BasementEntrances") ?? new Dictionary<ulong, BasementEntrance>();
            BasementStructures = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<uint, BasementStructure>>($"Basements/BasementStructures") ?? new Dictionary<uint, BasementStructure>();
            Puts($"Loaded {BasementStructures.Count} structures {BasementEntrances.Count} entrances {BasementEntities.Count} entities");
        }
        private List<string> _subscriptions = new List<string>();
        public void UnsubscribeAll(params string[] methods) {
            _subscriptions.AddRange(methods);
            foreach (var method in _subscriptions) {
                Unsubscribe(method);
            }
        }
        public void SubscribeAll() {
            foreach (var method in _subscriptions) {
                switch (method) {
                    default : Subscribe(method);
                    break;
                }
            }
        }
    }
}
namespace Oxide.Plugins {
    public partial class Basements {
        [HookMethod("IsBasementEntity")]
        private bool API_IsBasementEntity(ulong entityId) {
            return BasementEntities.Contains(entityId);
        }
        [HookMethod("GetBasementBuildingIds")]
        private uint[] API_GetBasementBuildingIds(uint surfaceBuildingId) {
            var surfaceBuilding = BuildingManager.server.GetBuilding(surfaceBuildingId);
            if (surfaceBuilding == null) {
                return Array.Empty<uint>();
            }
            var list = GetBasementsConnectedToSurfaceBuilding(surfaceBuilding);
            var result = new uint[list.Count];
            for (int i = 0; i < list.Count; i++) {
                result[i] = list[i].BasementBuildingId;
            }
            return result;
        }
        [HookMethod("GetSurfaceBuildingIds")]
        private uint[] API_GetSurfaceBuildingIds(uint basementBuildingId) {
            var basement = BasementStructures.GetValueOrDefault(basementBuildingId);
            if (basement == null) {
                return Array.Empty<uint>();
            }
            return basement.SurfaceBuildingIds.ToArray();
        }
    }
}
namespace Oxide.Plugins {
    public partial class Basements {
        // Key is basement building id
        public Dictionary<uint, BasementStructure> BasementStructures = new Dictionary<uint, BasementStructure>();
        // Key is hatch id
        public Dictionary<ulong, BasementEntrance> BasementEntrances = new Dictionary<ulong, BasementEntrance>();
        // Key is entity id
        public HashSet<ulong> BasementEntities = new HashSet<ulong>();
#region Models
        public class BasementEntrance {
            public ulong HatchId;
            public bool IsDownHatch;
            public ulong ConnectedHatchId;
            [JsonIgnore]
            private Door _baseEntity = null;
            [JsonIgnore]
            public Door BaseEntity {
                get {
                    if (_baseEntity == null) {
                        _baseEntity = FindBaseEntity(HatchId) as Door;
                    }
                    return _baseEntity;
                }
            }
            [JsonIgnore]
            private BasementEntrance _connectedHatch = null;
            [JsonIgnore]
            public BasementEntrance ConnectedHatch {
                get {
                    if (_connectedHatch == null) {
                        var door = FindBaseEntity(ConnectedHatchId) as Door;
                        _connectedHatch = door == null ? null : INSTANCE.BasementEntrances.GetValueOrDefault(door.net.ID.Value);
                    }
                    return _connectedHatch;
                }
            }
            [JsonIgnore]
            private BuildingBlock _parentBuildingBlock = null;
            [JsonIgnore]
            public BuildingBlock ParentBuildingBlock {
                get {
                    if (_parentBuildingBlock == null) {
                        _parentBuildingBlock = BaseEntity?.GetParentEntity() as BuildingBlock;
                    }
                    return _parentBuildingBlock;
                }
            }
            [JsonIgnore]
            public uint BasementBuildingId => (IsDownHatch ? ConnectedHatch?.ParentBuildingBlock?.buildingID : ParentBuildingBlock?.buildingID) ?? 0;
            [JsonIgnore]
            public BuildingManager.Building BasementBuilding => BuildingManager.server.GetBuilding(BasementBuildingId);
            [JsonIgnore]
            public BasementStructure Basement => INSTANCE.BasementStructures.GetValueOrDefault(BasementBuildingId);
            [JsonIgnore]
            public uint SurfaceBuildingId => (IsDownHatch ? ParentBuildingBlock?.buildingID : ConnectedHatch?.ParentBuildingBlock?.buildingID) ?? 0;
            [JsonIgnore]
            public BuildingManager.Building SurfaceBuilding => BuildingManager.server.GetBuilding(SurfaceBuildingId);
            public void ClearCache() {
                _baseEntity = null;
                _connectedHatch = null;
                _parentBuildingBlock = null;
            }
        }
        public class BasementStructure {
            public uint BasementBuildingId;
            public float Elevation;
            public bool ShownTip;
            [JsonIgnore]
            public BuildingManager.Building BasementBuilding => BuildingManager.server.GetBuilding(BasementBuildingId);
            [JsonIgnore]
            public IEnumerable<BuildingBlock> BasementBlocks => BasementBuilding?.buildingBlocks.Where(x => x.IsBasementEntity()) ?? Array.Empty<BuildingBlock>();
            [JsonIgnore]
            private List<BasementEntrance> _entrances = null;
            [JsonIgnore]
            public int EntrancePairCount = 0;
            [JsonIgnore]
            public IEnumerable<BasementEntrance> Entrances {
                get {
                    if (_entrances == null) {
                        EntrancePairCount = 0;
                        _entrances = new List<BasementEntrance>();
                        if (BasementBuilding != null) {
                            foreach (var decay in BasementBuilding.decayEntities) {
                                if (decay != null && decay.IsBasementEntrance()) {
                                    var entrance = INSTANCE.BasementEntrances[decay.net.ID.Value];
                                    _entrances.Add(entrance);
                                    EntrancePairCount++;
                                    var connectedEntrance = entrance.ConnectedHatch;
                                    if (connectedEntrance != null) {
                                        _entrances.Add(connectedEntrance);
                                    }
                                }
                            }
                        }
                    }
                    return _entrances;
                }
            }
            [JsonIgnore]
            public IEnumerable<uint> SurfaceBuildingIds {
                get {
                    HashSet<uint> ids = new HashSet<uint>();
                    foreach (var entrance in Entrances) {
                        if (entrance.IsDownHatch) {
                            ids.Add(entrance.SurfaceBuildingId);
                        }
                    }
                    return ids;
                }
            }
            [JsonIgnore]
            public BuildingManager.Building FirstSurfaceBuilding => SurfaceBuildingIds.Any() == false ? null : BuildingManager.server.GetBuilding(SurfaceBuildingIds.First());
            public void ClearCache() {
                _entrances = null;
            }
        }
#endregion
#region Helper Methods
        public float GetProjectedBasementElevation(BuildingBlock start) {
            var foundations = GetFoundationsForBasement(start, BuildingGrade.Enum.Stone);
            // Find lowest foundation
            BuildingBlock lowestFoundation = null;
            foreach (var foundation in foundations) {
                if (lowestFoundation == null || lowestFoundation.transform.position.y > foundation.transform.position.y) {
                    lowestFoundation = foundation;
                }
            }
            var diff = (int)Math.Floor(start.transform.position.y - lowestFoundation.transform.position.y);
            var iseven = diff % 3 == 0;
            var elevation = lowestFoundation.transform.position.y + -6f;
            if (!iseven) {
                elevation -= 1.5f;
            }
            elevation -= 3f;
            // Uncomment if I ever want to disallow water basements
            //if (WaterLevel.Test(new Vector3(start.transform.position.x, elevation, start.transform.position.z), false, false))
            //{
            //    // If its in water, make it under the surface
            //    elevation = TerrainMeta.HeightMap.GetHeight(start.transform.position) - 6f;
            //}
            return elevation;
        }
        private List<BasementEntrance> _returnGetEntrancesForSurfaceBuilding = new List<BasementEntrance>();
        public List<BasementEntrance> GetDownEntrancesForSurfaceBuilding(BuildingManager.Building surfaceBuilding) {
            _returnGetEntrancesForSurfaceBuilding.Clear();
            if (surfaceBuilding == null || surfaceBuilding.buildingBlocks == null) {
                return _returnGetEntrancesForSurfaceBuilding;
            }
            foreach (var block in surfaceBuilding.buildingBlocks) {
                if (block == null) {
                    continue;
                }
                if (block.IsFoundation() && block.children != null) {
                    Door childHatch = null;
                    foreach (var child in block.children) {
                        if (child is Door door && door.IsBasementEntrance()) {
                            childHatch = door;
                            break;
                        }
                    }
                    if (childHatch != null && childHatch.net != null) {
                        var entrance = INSTANCE.BasementEntrances[childHatch.net.ID.Value];
                        if (entrance != null) {
                            _returnGetEntrancesForSurfaceBuilding.Add(entrance);
                        }
                    }
                }
            }
            return _returnGetEntrancesForSurfaceBuilding;
        }
        private List<BasementStructure> _returnGetBasementsConnectedToSurfaceBuilding = new List<BasementStructure>();
        public List<BasementStructure> GetBasementsConnectedToSurfaceBuilding(BuildingManager.Building surfaceBuilding) {
            _returnGetBasementsConnectedToSurfaceBuilding.Clear();
            HashSet<uint> seen = new HashSet<uint>();
            foreach (var entrance in GetDownEntrancesForSurfaceBuilding(surfaceBuilding)) {
                var id = entrance.BasementBuildingId;
                if (id == 0 || !seen.Add(id)) continue;
                var basement = BasementStructures.GetValueOrDefault(id);
                if (basement != null) {
                    _returnGetBasementsConnectedToSurfaceBuilding.Add(basement);
                }
            }
            return _returnGetBasementsConnectedToSurfaceBuilding;
        }
#endregion
#region Main Methods
        public BasementStructure SpawnBasement(BuildingBlock surfaceFoundation) {
            if (surfaceFoundation == null) {
                return null;
            }
            var building = surfaceFoundation.GetBuilding();
            var basements = GetBasementsConnectedToSurfaceBuilding(building);
            BasementStructure basement = null;
            if (basements.Count != 0) {
                // Try to get existing basement that exists directly under this foundation
                basement = basements.Select(x => x.BasementBuilding?.GetBasementFoundationFromSurface(surfaceFoundation)).FirstOrDefault(x => x != null)?.GetBasement();
            }
            if (basement == null) {
                // Spawn new basement because there isn't one under this foundation
                basement = new BasementStructure {
                    // If there are other basements, we want the elevations to match
                    Elevation = basements.Count == 0 ? GetProjectedBasementElevation(surfaceFoundation) : basements.First().Elevation, BasementBuildingId = BuildingManager.server.NewBuildingID()
                }
                ;
                // Spawn basement cell at elevation
                var basementFoundation = Spawn1x1BasementCell(basement, surfaceFoundation);
                var effect = new Effect(Effects.PromoteStone, surfaceFoundation, 0, Vector3.zero, Vector3.zero);
                effect.Play();
                timer.Repeat(0.5f, 2, () => {
                    effect.Play();
                }
                );
                BasementStructures[basement.BasementBuildingId] = basement;
            }
            // Spawn ladder hatches
            timer.In(1f, () =>  // Allow animations to finish
            {
                if (surfaceFoundation == null) {
                    return;
                }
                SpawnLadderHatchPair(basement, surfaceFoundation, out _, out _);
                basement?.ClearCache();
            }
            );
            return basement;
        }
#endregion
        public void SpawnMissingExteriorWalls(BasementStructure basement) {
            if (basement == null) {
                return;
            }
            foreach (var block in basement.BasementBlocks) {
                if (block.IsNetNull() || !block.IsFoundation() || !block.IsBasementEntity()) {
                    continue;
                }
                var triangle = block.IsTriangle();
                for (int d = 1; d <= (triangle ? 3 : 4);
                d++) {
                    // check if theres a foundation in that direction
                    var foundation = block.GetLinkedFoundation(d);
                    if (foundation == null) {
                        // spawn a wall if theres no foundation and no wall
                        var wall = block.GetLinkedWall(d, out Vector3 pos, out Vector3 rot);
                        if (wall == null) {
                            SpawnBasementBlock(basement, Prefabs.Wall, block, position : block.transform.position, rotation : block.transform.rotation, localPosition : pos, localRotation : rot, ownerid : block.OwnerID );
                        }
                    }
                }
                // check if theres a ceiling
                var ceiling = block.GetCeilingAboveFoundation();
                if (ceiling == null) {
                    SpawnBasementBlock(basement, triangle ? Prefabs.TriangleFloor : Prefabs.Floor, block, position : block.transform.position, rotation : block.transform.rotation, localPosition : new Vector3(0, 3f, 0), ownerid : block.OwnerID );
                }
            }
        }
        public void DestroyBasementCell(BuildingBlock basementFoundation) {
            if (basementFoundation != null) {
                TeleportBasementContents(basementFoundation);
                var ceiling = basementFoundation.GetCeilingAboveFoundation();
                KillBasementEntity(ceiling);
                for (int d = 1; d <= 4; d++) {
                    var wall = basementFoundation.GetLinkedWall(d, out Vector3 pos, out Vector3 rot);
                    KillBasementEntity(wall);
                }
                KillBasementEntity(basementFoundation);
            }
        }
        private BuildingBlock GetSurfaceFoundation(BuildingBlock basementFoundation, BuildingManager.Building surfaceBuilding) {
            if (basementFoundation == null) {
                return null;
            }
            foreach (var block in surfaceBuilding?.buildingBlocks) {
                if (block == null || !block.ShortPrefabName.Contains("foundation") || block.IsBasementEntity()) {
                    continue;
                }
                if (EqualsPosition2D(basementFoundation.WorldSpaceBounds().ToBounds().center, block.WorldSpaceBounds().ToBounds().center)) {
                    return block;
                }
            }
            return null;
        }
        private BuildingBlock GetBasementFoundation(BuildingBlock surfaceFoundation, BuildingManager.Building basementBuilding) {
            if (surfaceFoundation == null) {
                return null;
            }
            foreach (var block in basementBuilding?.buildingBlocks) {
                if (block == null || !block.ShortPrefabName.Contains("foundation") || block.IsBasementEntity()) {
                    continue;
                }
                if (EqualsPosition2D(block.transform.position, surfaceFoundation.transform.position)) {
                    return block;
                }
            }
            return null;
        }
        private BuildingBlock GetBasementFoundation(BuildingBlock surfaceFoundation) {
            if (surfaceFoundation == null) {
                return null;
            }
            return RaycastVertical(surfaceFoundation, Vector3.down, 100f, (x) => (x is BuildingBlock casted && casted.IsBasementEntity() && casted.IsFoundation())) as BuildingBlock;
        }
        private void DestroyBlocksAbove(BaseEntity entity, float distance) {
            foreach(var hit in GetEntitiesAbove(entity, distance)) {
                if (!(hit is StabilityEntity block)) {
                    continue;
                }
                hit.Kill(BaseNetworkable.DestroyMode.Gib);
            }
        }
        List<BaseEntity> hitList = new List<BaseEntity>();
        List<BaseEntity> GetEntitiesAbove(BaseEntity block, float height) {
            Vector3 startPos = block.CenterPoint().Mod(y : 0.4f);
            hitList.Clear();
            float radius = 0.3f;
            foreach (var hit in Physics.SphereCastAll(startPos, radius, Vector3.up, height)) {
                hitList.Add(hit.GetEntity());
            }
            return hitList;
        }
        public void KillBasementEntity(BaseEntity entity, BasementStructure basement = null, bool gibs = false) {
            if (entity == null || entity.IsDestroyed) {
                return;
            }
            var eid = entity.net.ID.Value;
            BasementEntities.Remove(eid);
            destroying = true;
            entity.Kill(gibs ? BaseNetworkable.DestroyMode.Gib : BaseNetworkable.DestroyMode.None);
            if (entity is DecayEntity de) {
                BuildingManager.server.Remove(de);
            }
            destroying = false;
        }
        public void DestroyBasementEntrancePair(BasementEntrance entrance, bool gibs = false) {
            if (entrance == null) {
                return;
            }
            var connected = entrance.ConnectedHatch;
            KillBasementEntity(entrance.BaseEntity, gibs : gibs);
            KillBasementEntity(connected?.BaseEntity, gibs : gibs);
            BasementEntrances.Remove(entrance.HatchId);
            if (connected != null) {
                BasementEntrances.Remove(connected.HatchId);
            }
        }
        public void DestroyBasement(BasementStructure basement) {
            if (basement == null) {
                return;
            }
            var buildingId = basement.BasementBuildingId;
            var blocks = basement.BasementBlocks?.ToArray() ?? Array.Empty<BuildingBlock>();
            basement?.ClearCache();
            foreach (var entrance in basement.Entrances.ToArray()) {
                DestroyBasementEntrancePair(entrance, gibs : false);
            }
            foreach (var block in blocks) {
                if (block.IsNetNull() || !block.IsBasementEntity() || !block.IsFoundation()) {
                    continue;
                }
                // Move all entities
                TeleportBasementContents(block);
            }
            foreach (var entity in blocks) {
                if (entity.IsNetNull() || !entity.IsBasementEntity()) {
                    continue;
                }
                // Kill
                try {
                    foreach (var child in entity.children) {
                        if (child == null) {
                            continue;
                        }
                        KillBasementEntity(child, basement);
                    }
                    KillBasementEntity(entity, basement);
                }
                catch (Exception) {
                    // Failed to kill exterior entity
                }
            }
            BasementStructures.Remove(buildingId);
        }
        public int GetDirOfWall(BuildingBlock foundation, BuildingBlock targetWall) {
            if (foundation == null || targetWall == null) {
                return 0;
            }
            for(int i = 1; i <= 4; i++) {
                var wall = foundation.GetLinkedWall(i, out _, out _);
                if (wall == null) {
                    continue;
                }
                if (wall.EqualNetID(targetWall)) {
                    return i;
                }
            }
            return 0;
        }
        private readonly List<BaseCombatEntity> _nearbyCombatEntities = new List<BaseCombatEntity>();
        public void TeleportBasementContents(BuildingBlock basementFoundation) {
            if (basementFoundation.IsFoundation()) {
                _nearbyCombatEntities.Clear();
                Vis.Entities(basementFoundation.transform.position, 2f, _nearbyCombatEntities);
                foreach (var target in _nearbyCombatEntities) {
                    try {
                        if (target == null || target.EqualNetID(basementFoundation) || target is BuildingBlock || target.IsBasementEntity() || !IsBelowTerrain(target)) {
                            continue;
                        }
                        var kill = true;
                        if (target is BasePlayer basePlayer) {
                            if (basePlayer.IsGod()) {
                                kill = false;
                            }
                        }
                        try {
                            target.transform.position = target.transform.position.Set(y : GetTerrainHeightAtPosition(target.transform.position) + 2f);
                            target.SendNetworkUpdateImmediate();
                        }
                        catch (Exception) {
                            // Could not move entity
                        }
                        if (kill) {
                            target.Die();
                        }
                    }
                    catch (Exception) {
                        // Failed to kill interior entity
                    }
                }
            }
        }
        void SpawnLadderHatchPair(BasementStructure basement, BuildingBlock surfaceFoundation, out BasementEntrance downHatch, out BasementEntrance upHatch) {
            Vector3 position = surfaceFoundation.transform.position + new Vector3(0f, 0.0f, 0f);
            Quaternion rotation = surfaceFoundation.transform.rotation;
            // Spawn down hatch
            var downdoor = SpawnBasementBlock(basement : basement, prefab : Prefabs.LadderHatch, parent : surfaceFoundation, position : position, rotation : rotation, localRotation : new Vector3(0, 0, 180f), localPosition : new Vector3(0, 0.15f, 0f), setParent : true, buildingId : surfaceFoundation.buildingID, ownerid : surfaceFoundation.OwnerID) as Door;
            downHatch = new BasementEntrance {
                IsDownHatch = true, HatchId = downdoor.net.ID.Value
            }
            ;
            BasementEntrances[downHatch.HatchId] = downHatch;
            // Spawn up hatch
            var entitiesInRadius = GetEntitiesInRadius<BuildingBlock>(surfaceFoundation.transform.position.Set(y : basement.Elevation + 3f), 0.05f);
            var ceiling = entitiesInRadius.FirstOrDefault(x => x.ShortPrefabName.Contains("floor"));
            Pool<BuildingBlock>.Recycle(entitiesInRadius);
            var updoor= SpawnBasementBlock(basement : basement, prefab : Prefabs.LadderHatch, parent : ceiling, position : ceiling.transform.position.Mod(y : -0.15f), rotation : ceiling.transform.rotation, setParent : true, ownerid : surfaceFoundation.OwnerID) as Door;
            upHatch = new BasementEntrance {
                IsDownHatch = false, HatchId = updoor.net.ID.Value
            }
            ;
            BasementEntrances[upHatch.HatchId] = upHatch;
            // Link hatches
            downHatch.ConnectedHatchId = upHatch.HatchId;
            upHatch.ConnectedHatchId = downHatch.HatchId;
            var effect = new Effect(Effects.PlaceFrame, downdoor, 0, Vector3.zero, Vector3.zero);
            effect.Play();
        }
        public BuildingBlock Spawn1x1BasementCell(BasementStructure basement, BuildingBlock surfaceFoundation) {
            var foundation = GetBasementFoundation(surfaceFoundation);
            if (foundation == null) {
                foundation = SpawnBasementBlock(basement, Prefabs.Foundation, surfaceFoundation, ownerid : surfaceFoundation.OwnerID) as BuildingBlock;
            }
            // Spawn walls if there isnt one
            for (int d = 1; d <= 4; d++) {
                // check if theres a foundation in that direction
                var adjFoundation = foundation.GetLinkedFoundation(d);
                if (adjFoundation == null) {
                    // spawn a wall if theres no foundation and no wall
                    var wall = foundation.GetLinkedWall(d, out Vector3 pos, out Vector3 rot);
                    if (wall == null) {
                        SpawnBasementBlock(basement, Prefabs.Wall, foundation, position : foundation.transform.position, rotation : foundation.transform.rotation, localPosition : pos, localRotation : rot, ownerid : foundation.OwnerID );
                    }
                }
                else if (adjFoundation != null) {
                    // remove the wall in this direction if its a basement entity
                    var wall = foundation.GetLinkedWall(d, out Vector3 pos, out Vector3 rot);
                    if (wall != null && wall.IsBasementEntity()) {
                        KillBasementEntity(wall, basement, gibs : true);
                    }
                }
            }
            var ceiling = foundation.GetCeilingAboveFoundation();
            if (ceiling == null) {
                SpawnBasementBlock(basement, Prefabs.Floor, surfaceFoundation, localPosition : new Vector3(0f, 3f, 0f), localRotation : new Vector3(0, 0, 0), ownerid : foundation.OwnerID);
            }
            return foundation;
        }
        public void Spawn1x1BasementCellTriangle(BasementStructure basement, BuildingBlock surfaceFoundation) {
            var foundation = GetBasementFoundation(surfaceFoundation);
            if (foundation == null) {
                foundation = SpawnBasementBlock(basement, Prefabs.TriangleFoundation, surfaceFoundation) as BuildingBlock;
            }
            // Spawn walls if there isnt one
            for (int d = 1; d <= 3; d++) {
                // check if theres a foundation in that direction
                var adjFoundation = foundation.GetLinkedFoundation(d);
                if (adjFoundation == null) {
                    // spawn a wall if theres no foundation and no wall
                    var wall = foundation.GetLinkedWall(d, out Vector3 pos, out Vector3 rot);
                    if (wall == null) {
                        SpawnBasementBlock(basement, Prefabs.Wall, foundation, position : foundation.transform.position, rotation : foundation.transform.rotation, localPosition : pos, localRotation : rot, ownerid : foundation.OwnerID );
                    }
                }
            }
            var ceiling = foundation.GetCeilingAboveFoundation();
            if (ceiling == null) {
                SpawnBasementBlock(basement, Prefabs.TriangleFloor, surfaceFoundation, localPosition : new Vector3(0f, 3f, 0f), ownerid : foundation.OwnerID);
            }
        }
        public BaseEntity SpawnBasementBlock(BasementStructure basement, string prefab, StabilityEntity parent, Vector3? position = null, Quaternion? rotation = null, Vector3? localPosition = null, Vector3? localRotation = null, bool setParent = false, bool applyGrade = true, ulong ownerid = 0, uint? buildingId = null) {
            var entity = GameManager.server.CreateEntity(prefab, position ?? parent.transform.position.Set(y : basement.Elevation), rotation ?? parent.transform.rotation) as StabilityEntity;
            if (localPosition != null) {
                entity.transform.Translate(localPosition.Value);
            }
            if (localRotation != null) {
                entity.transform.Rotate(localRotation.Value);
            }
            if (entity is BuildingBlock block) {
                block.blockDefinition = PrefabAttribute.server.Find<Construction>(entity.prefabID);
                if (applyGrade) {
                    block.grade = config.BasementBuildingGrade;
                }
                else {
                    block.grade = BuildingGrade.Enum.Wood;
                }
            }
            entity.grounded = true;
            entity.OwnerID = ownerid;
            entity.AttachToBuilding(buildingId == null ? basement.BasementBuildingId : buildingId.Value);
            entity.Spawn();
            entity.Heal(10000f);
            if (setParent) {
                entity.SetParent(parent, worldPositionStays : true, sendImmediate : true);
            }
            BasementEntities.Add(entity.net.ID.Value);
            return entity;
        }
        public HashSet<ulong> CachedEntitiesBelowTerrain = new HashSet<ulong>();
        public bool IsBelowTerrain(BaseEntity entity) {
            if (entity == null || entity.net == null) {
                return false;
            }
            if (CachedEntitiesBelowTerrain.Contains(entity.net.ID.Value)) {
                return true;
            }
            Vector3 position = entity.transform.position;
            float terrainHeight = TerrainMeta.HeightMap.GetHeight(position);
            if (position.y < terrainHeight - 3f) {
                CachedEntitiesBelowTerrain.Add(entity.net.ID.Value);
                return true;
            }
            return false;
        }
        public BaseLock GetLockOnDoor(Door door) {
            BaseLock @lock = null;
            foreach (var child in door.children) {
                if (child is BaseLock) {
                    @lock = (BaseLock)child;
                    break;
                }
            }
            return @lock;
        }
        public BaseLock SpawnLockOnDoor(Door door, BaseLock clone, bool replaceOldLock = true) {
            if (replaceOldLock) {
                BaseLock oldLock = GetLockOnDoor(door);
                lockDestroyingInProgress = true;
                oldLock?.Kill();
                lockDestroyingInProgress = false;
            }
            BaseLock newLock = null;
            if (clone is CodeLock cloneCodeLock) {
                var newCodeLock = GameManager.server.CreateEntity("assets/prefabs/locks/keypad/lock.code.prefab") as CodeLock;
                newCodeLock.gameObject.Identity();
                newCodeLock.SetParent(door, door.GetSlotAnchorName(BaseEntity.Slot.Lock));
                newCodeLock.Spawn();
                newCodeLock.code = cloneCodeLock.code;
                newCodeLock.hasCode = cloneCodeLock.hasCode;
                newCodeLock.guestCode = cloneCodeLock.guestCode;
                newCodeLock.hasGuestCode = cloneCodeLock.hasGuestCode;
                newCodeLock.whitelistPlayers = cloneCodeLock.whitelistPlayers;
                Effect.server.Run("assets/prefabs/locks/keypad/effects/lock-code-deploy.prefab", newLock.transform.position);
                newLock = newCodeLock;
            }
            else if (clone is KeyLock cloneKeyLock) {
                var newKeyLock = GameManager.server.CreateEntity("assets/prefabs/locks/keylock/lock.key.prefab") as KeyLock;
                newKeyLock.gameObject.Identity();
                newKeyLock.SetParent(door, door.GetSlotAnchorName(BaseEntity.Slot.Lock));
                newKeyLock.Spawn();
                newKeyLock.keyCode = cloneKeyLock.keyCode;
                newLock = newKeyLock;
            }
            if (newLock != null) {
                newLock.SetFlag(BaseEntity.Flags.Locked, clone.HasFlag(BaseEntity.Flags.Locked));
                newLock.OwnerID = clone.OwnerID;
                door.SetSlot(BaseEntity.Slot.Lock, newLock);
            }
            return newLock;
        }
        public void GetBuildingBlocksBelowTerrain(float distance, List<BuildingBlock> blocksBelowTerrain) {
            foreach (var entity in BaseNetworkable.serverEntities) {
                if (entity is BuildingBlock block) {
                    Vector3 position = block.transform.position;
                    float terrainHeight = TerrainMeta.HeightMap.GetHeight(position);
                    if (position.y < terrainHeight - distance) {
                        blocksBelowTerrain.Add(block);
                    }
                }
            }
        }
        public bool IsWithinBasement(Vector3 position) {
            return RaycastFiltered(position, Vector3.up, 3f, out _, (hit) => {
                return hit is BuildingBlock block && block.IsBasementEntity() && block.IsCeiling();
            }
            );
        }
    }
}
namespace Oxide.Plugins.BasementMethods {
    public static partial class ExtensionMethods {
        public static Basements.BasementEntrance AsBasementEntrance(this Door door) => door == null ? null : Basements.INSTANCE.BasementEntrances.GetValueOrDefault(door.net.ID.Value);
        public static bool IsBasement(this BuildingManager.Building basementBuilding) => basementBuilding == null ? false : Basements.INSTANCE.BasementStructures.ContainsKey(basementBuilding.ID);
        public static Basements.BasementStructure AsBasement(this BuildingManager.Building basementBuilding) => basementBuilding == null ? null : Basements.INSTANCE.BasementStructures.GetValueOrDefault(basementBuilding.ID);
        public static IEnumerable<Basements.BasementStructure> GetBasements(this BuildingManager.Building surfaceBuilding) => Basements.INSTANCE.GetBasementsConnectedToSurfaceBuilding(surfaceBuilding);
        public static BuildingBlock GetBasementFoundationFromSurface(this IEnumerable<Basements.BasementStructure> basements, BuildingBlock surfaceFoundation) {
            if (surfaceFoundation == null) {
                return null;
            }
            return basements.Select(x => GetBasementFoundationFromSurface(x.BasementBuilding, surfaceFoundation)).FirstOrDefault();
        }
        public static BuildingBlock GetBasementFoundationFromSurface(this BuildingManager.Building basementBuilding, BuildingBlock surfaceFoundation) {
            if (surfaceFoundation == null) {
                return null;
            }
            foreach (var basementBlock in basementBuilding?.buildingBlocks) {
                if (basementBlock != null && basementBlock.IsBasementEntity() && basementBlock.IsFoundation() && basementBlock.Equals2DPosition(surfaceFoundation)) {
                    return basementBlock;
                }
            }
            return null;
        }
        public static BuildingBlock GetSurfaceFoundationFromBasement(this BuildingManager.Building surfaceBuilding, BuildingBlock basementFoundation) {
            if (basementFoundation == null) {
                return null;
            }
            foreach (var surfaceBlock in surfaceBuilding?.buildingBlocks) {
                if (surfaceBlock != null && surfaceBlock.IsFoundation() && surfaceBlock.Equals2DPosition(basementFoundation)) {
                    return surfaceBlock;
                }
            }
            return null;
        }
    }
}
namespace Oxide.Plugins {
    public partial class Basements {
        [Command("basements.getid"), Permission(PermissionAdmin)]
        private void CmdBasementsGetId(IPlayer player, string command, string[] args) {
            var basePlayer = player.Object as BasePlayer;
            if (basePlayer == null) {
                player.Message(Lang(player.Id, "command in game"));
                return;
            }
            var buildingblock = GetObjectRaycast(basePlayer, 100f) as BuildingBlock;
            if (buildingblock == null) {
                player.Message(Lang(player.Id, "command looking"));
                return;
            }
            var surfaceBuilding = buildingblock.GetBuilding();
            if (surfaceBuilding == null) {
                player.Message(Lang(player.Id, "command looking"));
                return;
            }
            var basements = GetBasementsConnectedToSurfaceBuilding(surfaceBuilding);
            if (basements?.Any() == false) {
                player.Message(Lang(player.Id, "command no basement"));
                return;
            }
            else {
                player.Message(Lang(player.Id, "command basements found", basements.Count, basements.Select(x => x.BasementBuildingId).ToSentence()));
                return;
            }
        }
        [Command("basements.destroy"), Permission(PermissionAdmin)]
        private void CmdBasementsDestroy(IPlayer player, string command, string[] args) {
            var usage = Lang(player.Id, "command usage", "basements.destroy <building id>");
            if (args.Length == 0) {
                player.Message(usage);
                return;
            }
            uint bid;
            if (!uint.TryParse(args[0], out bid)) {
                player.Message(Lang(player.Id, "command invalid bid"));
                return;
            }
            var basement = BasementStructures.GetValueOrDefault(bid);
            if (basement == null) {
                player.Message(Lang(player.Id, "command no basement for id", bid));
            }
            try {
                DestroyBasement(basement);
            }
            catch (Exception) {
                player.Message(Lang(player.Id, "command failed to destroy", bid));
                return;
            }
            player.Message(Lang(player.Id, "command destroy success", bid));
        }
        [Command("basements.destroyall"), Permission(PermissionAdmin)]
        private void CmdBasementsDestroyAll(IPlayer player, string command, string[] args) {
            player.Message(Lang(player.Id, "command destroy all"));
            int destroyed = 0;
            UnsubscribeAll();
            foreach (var basement in BasementStructures.Values.ToArray()) {
                try {
                    DestroyBasement(basement);
                    destroyed++;
                }
                catch (Exception) {
                    player.Message(Lang(player.Id, "command failed to destroy", basement?.BasementBuildingId));
                }
            }
            foreach (var entrance in BasementEntrances.Values) {
                entrance.BaseEntity?.Kill();
            }
            BasementStructures.Clear();
            BasementEntities.Clear();
            BasementEntrances.Clear();
            // Destroy any orphaned basements
            var blocksBelowTerrain = Pool<BuildingBlock>.Get();
            GetBuildingBlocksBelowTerrain(0.5f, blocksBelowTerrain);
            foreach (var block in blocksBelowTerrain) {
                try {
                    KillBasementEntity(block);
                }
                catch (Exception) {
                    // Ignore exception
                }
            }
            Pool<BuildingBlock>.Recycle(blocksBelowTerrain);
            player.Message(Lang(player.Id, "command destroy all success", destroyed));
            SubscribeAll();
        }
        [Command("basements.list"), Permission(PermissionAdmin)]
        private void CmdBasementsList(IPlayer player, string command, string[] args) {
            var msg = "BASEMENTS\n";
            foreach (var kvp in BasementStructures) {
                var basement = kvp.Value;
                basement.ClearCache();
                msg += $"KEY={kvp.Key} ID={basement.BasementBuildingId} ELE={basement.Elevation} DOORS {basement.Entrances?.Count() ?? 0}" + "\n";
            }
            msg += "\nENTRANCES\n";
            foreach (var kvp in BasementEntrances) {
                var entrance = kvp.Value;
                msg += $"KEY={kvp.Key} ID={entrance.HatchId} BASEMENT={entrance.BasementBuildingId} ISDOWN={entrance.IsDownHatch} Connected={entrance.ConnectedHatchId} ConnectedExists={entrance.ConnectedHatch != null}\n";
            }
            player.Message(msg);
        }
    }
}
namespace Oxide.Plugins {
    public partial class Basements {
        private Configuration config;
        private class Configuration {
            [JsonProperty(PropertyName = "Basement Entrance Build Cost")]
            public string BasementEntranceBuildCost = "300 metal.fragments";
            [JsonProperty(PropertyName = "Basement Cell Build Cost (Square)")]
            public string BasementSquareCellBuildCost = "500 stones";
            [JsonProperty(PropertyName = "Basement Cell Build Cost (Triangle)")]
            public string BasementTriangleCellBuildCost = "250 stones";
            [JsonProperty(PropertyName = "Basement Cell Building Grade (Mostly Cosmetic)")]
            [JsonConverter(typeof(StringEnumConverter))]
            public BuildingGrade.Enum BasementBuildingGrade = BuildingGrade.Enum.Stone;
            [JsonProperty(PropertyName = "Required Foundation Grade For Basements")]
            [JsonConverter(typeof(StringEnumConverter))]
            public BuildingGrade.Enum RequiredFoundationGrade = BuildingGrade.Enum.Stone;
            [JsonProperty(PropertyName = "Max Basement Entrances Per Building")]
            public int EntranceLimit = 3;
            [JsonProperty(PropertyName = "Damage Required To Break Locks on Basement Entrances")]
            public float DamageRequiredToBreakLock = 50f;
            [JsonProperty(PropertyName = "Damage Required To Drill New Basement Cells")]
            public float DamageRequiredToDrillBasement = 5f;
            [JsonProperty(PropertyName = "Destroy Basement Cell When Surface Foundation is Destroyed")]
            public bool DestroyBasementUnderFoundation = true;
            [JsonProperty(PropertyName = "Allow Hatch Removal (requires other plugins)")]
            public bool AllowHatchRemoval = false;
            [JsonProperty(PropertyName = "Restrict Drill Items")]
            public bool RestrictDrillItems = true;
            [JsonProperty(PropertyName = "Sync Locks on Basement Entrances")]
            public bool SyncLocksOnBasementEntrances = true;
            [JsonProperty(PropertyName = "Allowed Items for Drilling (if Restrict Drill Items is true)")]
            public string[] BasementDrillItems = new string[] {
                "jackhammer"
            }
            ;
            [JsonProperty(PropertyName = "Forbidden Deployables in Basement (full prefab path)")]
            public string[] ForbiddenDeployablesInBasement = new string[] {
                "assets/prefabs/deployable/tool cupboard/shockbyte/cupboard.tool.shockbyte.deployed.prefab", "assets/prefabs/deployable/cupboard.tool.retro.deployed.prefab", "assets/prefabs/deployable/tool cupboard/cupb
// ... [Code preview truncated due to length. Full source in attached file] ...

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