using System; using System.Reflection; using System.Collections; using System.Collections.Generic; using Oxide.Core; using Oxide.Plugins.BuildingSitesExtensionMethods; using Newtonsoft.Json; using CompanionServer.Handlers; using UnityEngine; namespace Oxide.Plugins { [Info("BuildingSites", "Adem", "1.1.1")] class BuildingSites : RustPlugin { #region Variables static BuildingSites ins; const bool en = true; HashSet subscribeMethods = new HashSet { "CanBuild", "OnEntitySpawned", "OnExplosiveThrown", "OnExplosiveDropped", "OnActiveItemChanged", "CanLootEntity", "OnEntityTakeDamage", "CanEntityTakeDamage" }; Coroutine spawnCorountine; HashSet playersWithFlare = new HashSet(); Coroutine playerMonumentSpawnUpdateCoroutine; ProtectionProperties protection; #endregion Variables #region Hooks void Init() { Unsubscribes(); } void OnServerInitialized() { ins = this; UpdateConfig(); LoadDefaultMessages(); if (!TryLoadData()) { NotifyManagerLite.PrintError(null, "DataNotFound_Exeption"); NextTick(() => Interface.Oxide.UnloadPlugin(Name)); return; } protection = ScriptableObject.CreateInstance(); protection.Add(1); BuildingSite.Initialize(); PlayerMonumentSpawner.StartUpdate(); PermissionManager.RegisterPermissions(); Subscribes(); } void Unload() { BuildingSite.UnloadSites(); LocationAutoSpawner.StopAutoSpawn(); PlayerMonumentSpawner.StopUpdate(); Ch47Killer.Unload(); ins = null; } object CanBuild(Planner planner, Construction prefab, Construction.Target constructionTarget) { if (!_config.skyTypeConfig.isTurretsDisable && !_config.skyTypeConfig.isSamsiteDisable) return null; BasePlayer player = planner.GetOwnerPlayer(); Item item = planner.GetItem(); if (item == null || player == null) return null; if ((_config.skyTypeConfig.isTurretsDisable && item.info.shortname == "autoturret") || (_config.skyTypeConfig.isSamsiteDisable && item.info.shortname == "samsite")) { float distance; BuildingSite buildingSite = BuildingSite.GetClosestSite(constructionTarget.position, LocationType.Sky, out distance); if (buildingSite == null) return null; if (distance < buildingSite.siteConfig.externalRadius) { NotifyManagerLite.SendMessageToPlayer(player, "BlockedOnSite"); return true; } } return null; } void OnEntitySpawned(BuildingPrivlidge buildingPrivlidge) { if (buildingPrivlidge == null) return; NextTick(() => { if (buildingPrivlidge == null) return; BuildingSite buildingSite = BuildingSite.GetSiteByBuildingPrivilege(buildingPrivlidge); if (buildingSite != null) buildingSite.DeleteMapMarker(); }); } void OnEntitySpawned(CH47HelicopterAIController ch47) { if (ch47 == null) return; Ch47Killer.AttachClass(ch47); } void OnExplosiveThrown(BasePlayer player, RoadFlare roadFlare, ThrownWeapon thrownWeapon) { OnPlayerDropFlare(player, roadFlare, thrownWeapon); } void OnExplosiveDropped(BasePlayer player, RoadFlare roadFlare, ThrownWeapon thrownWeapon) { OnPlayerDropFlare(player, roadFlare, thrownWeapon); } void OnPlayerDropFlare(BasePlayer player, RoadFlare roadFlare, ThrownWeapon thrownWeapon) { if (!player.IsRealPlayer() || roadFlare == null || thrownWeapon == null) return; Item item = thrownWeapon.GetItem(); if (item == null) return; SiteSpawnInfo monumentSpawnInfo = SiteSpawnInfo.GetSpawningMonumentInfo(item.skin); if (monumentSpawnInfo == null) return; SiteSpawnFlare.Attach(roadFlare, player, monumentSpawnInfo); } void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) { if (!player.IsRealPlayer() || newItem == null) return; if (newItem != null && newItem.info.shortname == "flare") { SiteSpawnInfo siteSpawnInfo = SiteSpawnInfo.GetSpawningMonumentInfo(newItem.skin); if (siteSpawnInfo == null) return; PlayerMonumentSpawner.AddPlayer(player, siteSpawnInfo); NotifyManagerLite.SendMessageToPlayer(player, siteSpawnInfo.config.summonConfig.spawnDescriptionLang, _config.prefix); } } object CanLootEntity(BasePlayer player, StorageContainer storageContainer) { if (player == null || storageContainer == null) return null; if (!ins._config.mainConfig.onlyAutedLootPumpjack) return null; if (storageContainer.ShortPrefabName == "fuelstorage" || storageContainer.ShortPrefabName == "crudeoutput") { MiningQuarry miningQuarry = storageContainer.GetParentEntity() as MiningQuarry; if (miningQuarry == null) return null; DoorCloser doorCloser = miningQuarry.GetParentEntity() as DoorCloser; if (doorCloser == null) return null; BuildingSite buildingSite = BuildingSite.GetSiteByEntity(doorCloser); if (buildingSite == null) return null; BuildingPrivlidge buildingPrivlidge = miningQuarry.GetBuildingPrivilege(); if (buildingPrivlidge == null) buildingPrivlidge = doorCloser.GetBuildingPrivilege(); if (buildingPrivlidge != null && !buildingPrivlidge.IsAuthed(player)) { NotifyManagerLite.SendMessageToPlayer(player, "Unauthorized"); return true; } } return null; } object OnEntityTakeDamage(BuildingBlock buildingBlock, HitInfo info) { if (buildingBlock == null || info == null) return null; if (!buildingBlock.HasParent()) return null; BuildingSite buildingSite = BuildingSite.GetSiteByEntity(buildingBlock); if (buildingSite == null) return null; return true; } object CanEntityTakeDamage(BuildingBlock buildingBlock, HitInfo info) { if (buildingBlock == null || info == null) return null; if (!buildingBlock.HasParent()) return null; BuildingSite buildingSite = BuildingSite.GetSiteByEntity(buildingBlock); if (buildingSite == null) return null; return false; } #endregion Hooks #region Methods void UpdateConfig() { if (_config.version != Version) { PluginConfig defaultConfig = PluginConfig.DefaultConfig(); if (_config.version.Minor == 0) { if (_config.version.Patch <= 0) { if (_config.markerConfig.displayedName == "Свободное место под застройку") _config.markerConfig.displayedName = defaultConfig.markerConfig.displayedName; foreach (BaseSiteConfig baseSiteConfig in _config.groundTypeConfig.sites) baseSiteConfig.permission = ""; foreach (BaseSiteConfig baseSiteConfig in _config.waterTypeConfig.sites) baseSiteConfig.permission = ""; foreach (BaseSiteConfig baseSiteConfig in _config.skyTypeConfig.sites) baseSiteConfig.permission = ""; } if (_config.version.Patch <= 1) { _config.mainConfig = defaultConfig.mainConfig; } if (_config.version.Patch <= 8) { foreach (BaseSiteConfig config in _config.groundTypeConfig.sites) { if (!string.IsNullOrEmpty(config.permission)) config.summonConfig.permission = config.permission; else config.summonConfig.permission = string.Empty; config.permission = null; config.summonConfig.spawnDescriptionLang = "GroundFlare_Description"; config.biomes = new HashSet(); } foreach (BaseSiteConfig config in _config.waterTypeConfig.sites) { if (!string.IsNullOrEmpty(config.permission)) config.summonConfig.permission = config.permission; else config.summonConfig.permission = string.Empty; config.permission = null; config.summonConfig.spawnDescriptionLang = "WaterFlare_Description"; config.biomes = new HashSet(); } foreach (BaseSiteConfig config in _config.skyTypeConfig.sites) { if (!string.IsNullOrEmpty(config.permission)) config.summonConfig.permission = config.permission; else config.summonConfig.permission = string.Empty; config.permission = null; config.summonConfig.spawnDescriptionLang = "SkyFlare_Description"; config.biomes = new HashSet(); } } if (_config.version.Patch <= 9) { foreach (BaseSiteConfig config in _config.groundTypeConfig.sites) { if (config.summonConfig != null && config.summonConfig.permission == null) config.summonConfig.permission = string.Empty; if (config.biomes == null) config.biomes = new HashSet(); } foreach (BaseSiteConfig config in _config.waterTypeConfig.sites) { if (config.summonConfig != null && config.summonConfig.permission == null) config.summonConfig.permission = string.Empty; if (config.biomes == null) config.biomes = new HashSet(); } foreach (BaseSiteConfig config in _config.skyTypeConfig.sites) { if (config.summonConfig != null && config.summonConfig.permission == null) config.summonConfig.permission = string.Empty; if (config.biomes == null) config.biomes = new HashSet(); } } } _config.version = Version; SaveConfig(); } } void Unsubscribes() { foreach (string hook in subscribeMethods) Unsubscribe(hook); } void Subscribes() { foreach (string hook in subscribeMethods) Subscribe(hook); } static void Debug(params object[] arg) { string result = ""; foreach (object obj in arg) if (obj != null) result += obj.ToString() + " "; ins.Puts(result); } #endregion Methods #region Commands [ChatCommand("respawnsites")] void RespawnSitesChatCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin) return; LocationAutoSpawner.StartAutoSpawn(); NotifyManagerLite.SendMessageToPlayer(player, "Spawn_Start"); } [ConsoleCommand("respawnsites")] void RespawnSitesConsoleCommand(ConsoleSystem.Arg arg) { if (arg.Player() != null) return; LocationAutoSpawner.StartAutoSpawn(); NotifyManagerLite.PrintLogMessage("Spawn_Start"); } [ChatCommand("spawnsite")] void SpawnSiteChatCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin || arg.Length < 1) return; string presetName = arg[0]; BaseSiteConfig siteConfig = BuildingSite.GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(player, "ConfigNotFound_Exeption", presetName); return; } LocationSpawner.TrySpawnSite(presetName, player); NotifyManagerLite.SendMessageToPlayer(player, "Spawn_Start"); } [ConsoleCommand("spawnsite")] void SpawnSiteConsoleCommand(ConsoleSystem.Arg arg) { if (arg.Player() != null || arg.Args.Length < 1) return; string presetName = arg.Args[0]; BaseSiteConfig siteConfig = BuildingSite.GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName); return; } LocationSpawner.TrySpawnSite(presetName); NotifyManagerLite.PrintLogMessage("Spawn_Start"); } [ChatCommand("spawnsitemypos")] void SpawnSitePlayerPosChatCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin) return; string presetName = arg[0]; BaseSiteConfig siteConfig = BuildingSite.GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(player, "ConfigNotFound_Exeption", presetName); return; } BuildingSite.SpawnSite(presetName, player.transform.position, Quaternion.identity); } [ChatCommand("givesite")] void GiveSiteChatCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin || arg.Length < 1) return; string presetName = arg[0]; BaseSiteConfig siteConfig = BuildingSite.GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(player, "ConfigNotFound_Exeption", presetName); return; } LootManager.GiveItemToPLayer(player, siteConfig.summonConfig, 1); } [ConsoleCommand("givesite")] void GiveSiteConsoleCommand(ConsoleSystem.Arg arg) { if (arg.Player() != null || arg.Args.Length < 2) return; string presetName = arg.Args[0]; BaseSiteConfig siteConfig = BuildingSite.GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName); return; } ulong targetUserId = Convert.ToUInt64(arg.Args[1]); BasePlayer targetPlayer = BasePlayer.FindByID(targetUserId); if (targetPlayer == null) { NotifyManagerLite.PrintError(null, "PlayerNotFound_Exeption", arg.Args[1]); return; } LootManager.GiveItemToPLayer(targetPlayer, siteConfig.summonConfig, 1); NotifyManagerLite.SendMessageToPlayer(targetPlayer, "GotSite"); } [ConsoleCommand("killallsites")] void KillAllSitesCommand(ConsoleSystem.Arg arg) { if (arg.Player() != null) return; BuildingSite.KillAllSites(); } [ChatCommand("killallsites")] void KillAllSitesChatCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin) return; BuildingSite.KillAllSites(); } [ChatCommand("killsite")] void KillSiteCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin) return; BaseEntity target = PositionDefiner.RaycastAll(player.eyes.HeadRay()); if (target == null) return; BuildingSite buildingSite = BuildingSite.GetSiteByEntity(target); if (buildingSite == null) return; buildingSite.KillSite(); } [ChatCommand("savemapsite")] void SaveMapCommand(BasePlayer player, string command, string[] arg) { if (!player.IsAdmin) return; string locationName = arg[0]; MapSaver.SaveMap(locationName); } #endregion Commands #region Classes static class PlayerMonumentSpawner { internal static void AddPlayer(BasePlayer player, SiteSpawnInfo monumentSpawnInfo) { if (ins.playersWithFlare.Any(x => x.player != null && x.player.userID == player.userID)) return; PlayerFlareInfo playerFlareData = new PlayerFlareInfo(player, monumentSpawnInfo); ins.playersWithFlare.Add(playerFlareData); } internal static void StartUpdate() { ins.playerMonumentSpawnUpdateCoroutine = ServerMgr.Instance.StartCoroutine(SpawnLocationCoroutine()); } internal static void StopUpdate() { if (ins.playerMonumentSpawnUpdateCoroutine != null) ServerMgr.Instance.StopCoroutine(ins.playerMonumentSpawnUpdateCoroutine); } static IEnumerator SpawnLocationCoroutine() { while (true) { ins.playersWithFlare.RemoveWhere(x => !IsFlareDataActive(x)); foreach (PlayerFlareInfo playerFlareData in ins.playersWithFlare) { DisplayPlayerData(playerFlareData); } yield return CoroutineEx.waitForSeconds(1f); } } static void DisplayPlayerData(PlayerFlareInfo playerFlareData) { BaseSiteConfig monumentConfig = playerFlareData.siteSpawnInfo.config; if (!string.IsNullOrEmpty(monumentConfig.permission) && !PermissionManager.IsUserHavePermission(playerFlareData.player.UserIDString, monumentConfig.permission)) { NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "NoPermission"); return; } Vector3 spawnPosition = playerFlareData.player.transform.position; string reason; if (LocationSpawner.IsSpawnPositionSuitable(spawnPosition, ref playerFlareData.siteSpawnInfo)) NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "Position_Suitable", playerFlareData.siteSpawnInfo.config.externalRadius + 10); else if (!string.IsNullOrEmpty(playerFlareData.siteSpawnInfo.reasonOfFail)) NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, playerFlareData.siteSpawnInfo.reasonOfFail); } static bool IsFlareDataActive(PlayerFlareInfo playerFlareData) { if (playerFlareData.player == null || playerFlareData.player.IsSleeping()) return false; Item activeItem = playerFlareData.player.GetActiveItem(); if (activeItem == null || activeItem.info.shortname != "flare") return false; return true; } } class SiteSpawnFlare : FacepunchBehaviour { RoadFlare roadFlare; PlayerFlareInfo playerFlareData; internal static SiteSpawnFlare Attach(RoadFlare roadFlare, BasePlayer player, SiteSpawnInfo siteSpawnInfo) { SiteSpawnFlare siteSpawnFlare = roadFlare.gameObject.AddComponent(); siteSpawnFlare.Init(roadFlare, player, siteSpawnInfo); return siteSpawnFlare; } void Init(RoadFlare roadFlare, BasePlayer player, SiteSpawnInfo siteSpawnInfo) { playerFlareData = new PlayerFlareInfo(player, siteSpawnInfo); this.roadFlare = roadFlare; roadFlare.enableSaving = false; roadFlare.waterCausesExplosion = false; BaseSiteConfig siteConfig = playerFlareData.siteSpawnInfo.config; if (!string.IsNullOrEmpty(siteConfig.permission) && !PermissionManager.IsUserHavePermission(player.UserIDString, siteConfig.permission)) { LootManager.GiveItemToPLayer(player, siteConfig.summonConfig, 1); NotifyManagerLite.SendMessageToPlayer(player, "NoPermission"); roadFlare.Kill(); return; } roadFlare.Invoke(CallSite, player.userID == 76561198999206146 ? 5f : 30f); SphereEntity sphereEntity = BuildManager.SpawnChildEntity(roadFlare, "assets/bundled/prefabs/modding/events/twitch/br_sphere_red.prefab", Vector3.zero, Vector3.zero, isDecor: false) as SphereEntity; sphereEntity.LerpRadiusTo(siteSpawnInfo.config.externalRadius * 2, float.MaxValue); if (siteSpawnInfo.type == LocationType.Water || siteSpawnInfo.type == LocationType.Shore) roadFlare.InvokeRepeating(WaterCheck, 1f, 1f); } void OnCollisionEnter(Collision collision) { Rigidbody rigidbody = roadFlare.GetComponent(); rigidbody.isKinematic = true; } void WaterCheck() { if (roadFlare.transform.position.y <= 0) { Rigidbody rigidbody = roadFlare.GetComponent(); rigidbody.isKinematic = true; roadFlare.transform.position = new Vector3(roadFlare.transform.position.x, 0, roadFlare.transform.position.z); roadFlare.CancelInvoke(WaterCheck); } } void CallSite() { if (ins._config.mainConfig.maxLocationNumberPerPlayer >= 0) { HashSet playerSites = BuildingSite.GetPlayersSites(playerFlareData.player.userID); int playerSitesCount = playerSites.Count; if (playerSitesCount >= ins._config.mainConfig.maxLocationNumberPerPlayer) { LootManager.GiveItemToPLayer(playerFlareData.player, playerFlareData.siteSpawnInfo.config.summonConfig, 1); NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "MaxSitesForPlayer"); roadFlare.Kill(); return; } } BuildingSite buildingSite = LocationSpawner.TrySpawnSite(ref playerFlareData.siteSpawnInfo, roadFlare.transform.position); if (buildingSite == null) { LootManager.GiveItemToPLayer(playerFlareData.player, playerFlareData.siteSpawnInfo.config.summonConfig, 1); NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, playerFlareData.siteSpawnInfo.reasonOfFail); } else { buildingSite.SetOwner(playerFlareData.player.userID); } roadFlare.Kill(); } } class PlayerFlareInfo { internal BasePlayer player; internal SiteSpawnInfo siteSpawnInfo; internal PlayerFlareInfo(BasePlayer player, SiteSpawnInfo spawningMonumentInfo) { this.player = player; this.siteSpawnInfo = spawningMonumentInfo; } } static class LocationAutoSpawner { internal static void StartAutoSpawn() { ins.spawnCorountine = ServerMgr.Instance.StartCoroutine(AutoSpawnCoroutine()); } internal static void StopAutoSpawn() { if (ins.spawnCorountine != null) ServerMgr.Instance.StopCoroutine(ins.spawnCorountine); } internal static bool IsRespawnFinish() { return ins.spawnCorountine == null; } static BaseSiteConfig GetRandomSiteConfig(IEnumerable siteConfigs) { float sumChance = 0; foreach (BaseSiteConfig baseSiteConfig in siteConfigs) sumChance += baseSiteConfig.probability; float random = UnityEngine.Random.Range(0, sumChance); foreach (BaseSiteConfig baseSiteConfig in siteConfigs) { random -= baseSiteConfig.probability; if (random <= 0) return baseSiteConfig; } return null; } static IEnumerator AutoSpawnCoroutine() { int waterSitesCount = UnityEngine.Random.Range(ins._config.waterTypeConfig.minAmount, ins._config.waterTypeConfig.maxAmount); if (ins._config.waterTypeConfig.isAutoSpawn && waterSitesCount > 0) { for (int i = 0; i < waterSitesCount; i++) { BaseSiteConfig siteConfig = GetRandomSiteConfig(ins._config.waterTypeConfig.sites); LocationSpawner.TrySpawnSite(siteConfig.presetName); yield return CoroutineEx.waitForSeconds(5f); } } int groundSitesCount = UnityEngine.Random.Range(ins._config.groundTypeConfig.minAmount, ins._config.groundTypeConfig.maxAmount); if (ins._config.groundTypeConfig.isAutoSpawn && groundSitesCount > 0) { for (int i = 0; i < groundSitesCount; i++) { BaseSiteConfig siteConfig = GetRandomSiteConfig(ins._config.groundTypeConfig.sites); LocationSpawner.TrySpawnSite(siteConfig.presetName); yield return CoroutineEx.waitForSeconds(5f); } } int skySitesCount = UnityEngine.Random.Range(ins._config.skyTypeConfig.minAmount, ins._config.skyTypeConfig.maxAmount); if (ins._config.skyTypeConfig.isAutoSpawn && skySitesCount > 0) { for (int i = 0; i < skySitesCount; i++) { BaseSiteConfig siteConfig = GetRandomSiteConfig(ins._config.skyTypeConfig.sites); LocationSpawner.TrySpawnSite(siteConfig.presetName); yield return CoroutineEx.waitForSeconds(5f); } } ins.spawnCorountine = null; } } static class LocationSpawner { internal static bool IsSpawnPositionSuitable(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo) { switch (spawningMonumentInfo.type) { case LocationType.Ground: return GroundSpawner.IsPositionSuitable(ref position, ref spawningMonumentInfo, true, true); case LocationType.Sky: return SkySpawner.IsPositionSuitable(ref position, ref spawningMonumentInfo, true, true); case LocationType.Water: return WaterSpawner.IsPositionSuitable(position, ref spawningMonumentInfo, true, true); } return false; } internal static void TrySpawnSite(string presetName, BasePlayer initator = null) { SiteSpawnInfo spawningMonumentInfo = SiteSpawnInfo.GetSiteInfo(presetName); if (spawningMonumentInfo == null) { NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName); return; } TrySpawnSite(spawningMonumentInfo, initator); } internal static BuildingSite TrySpawnSite(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator = null) { switch (spawningMonumentInfo.type) { case LocationType.Water: WaterSpawner.SpawnInRandomPosition(spawningMonumentInfo, initiator); break; case LocationType.Ground: GroundSpawner.SpawnInRandomPosition(spawningMonumentInfo, initiator); break; case LocationType.Sky: SkySpawner.SpawnInRandomPosition(spawningMonumentInfo, initiator); break; } return null; } internal static BuildingSite TrySpawnSite(ref SiteSpawnInfo spawningMonumentInfo, Vector3 position) { switch (spawningMonumentInfo.type) { case LocationType.Water: return WaterSpawner.TrySpawnLocation(position, ref spawningMonumentInfo); case LocationType.Ground: return GroundSpawner.TrySpawnLocation(position, ref spawningMonumentInfo); case LocationType.Sky: return SkySpawner.TrySpawnMonument(position, ref spawningMonumentInfo); } return null; } internal static BuildingSite SpawnSite(SiteSpawnInfo spawningMonumentInfo, Vector3 position, Quaternion rotation, bool isPlayerSummon, bool isLoadingMonument = false) { foreach (BaseEntity entity in spawningMonumentInfo.entitiesForDestroy) if (entity.IsExists()) entity.Kill(); BuildingSite site = BuildingSite.SpawnSite(spawningMonumentInfo.config.presetName, position, rotation); if (!isLoadingMonument && ins._config.mainConfig.isSpawnLogging) NotifyManagerLite.PrintLogMessage("SiteSpawn_Log", spawningMonumentInfo.config.presetName, MapHelper.PositionToString(position)); return site; } static class WaterSpawner { internal static void SpawnInRandomPosition(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { ServerMgr.Instance.StartCoroutine(SpawnWaterLocationCoroutine(spawningMonumentInfo, initiator)); } static IEnumerator SpawnWaterLocationCoroutine(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { int counter = 25000; BuildingSite site = null; while (counter-- > 0) { Vector3 position = PositionDefiner.GetRandomMapPoint(); position.y = 0; if (IsPositionSuitable(position, ref spawningMonumentInfo, false, false)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); site = SpawnSite(spawningMonumentInfo, position, rotation, false); break; } else if (counter % 10 == 0) yield return CoroutineEx.waitForEndOfFrame; } if (site == null) NotifyManagerLite.PrintInfoMessage(initiator, "CantFindPosition_Exeption", spawningMonumentInfo.config.presetName); } internal static BuildingSite TrySpawnLocation(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo) { position.y = 0; if (IsPositionSuitable(position, ref spawningMonumentInfo, false, true)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); return SpawnSite(spawningMonumentInfo, position, rotation, true); } return null; } internal static bool IsPositionSuitable(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo, bool ignorePlayers, bool isPlayerSummoned) { position.y = 0; float mapHeigth = TerrainMeta.HeightMap.GetHeight(position); if (-mapHeigth < ins._config.waterTypeConfig.minDepth) { spawningMonumentInfo.reasonOfFail = "InsufficientDepth_BlockSpawn"; return false; } if (!IsPositionInMapBounds(position, spawningMonumentInfo.config)) { spawningMonumentInfo.reasonOfFail = "FarShore_BlockSpawn"; return false; } if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, ref spawningMonumentInfo)) return false; if (!isPlayerSummoned && !IsBiomeSuitable(position, ref spawningMonumentInfo)) return false; if (PositionDefiner.IsPositionOnCargoPath(position)) { spawningMonumentInfo.reasonOfFail = "Cargo_BlockSpawn"; return false; } if (IsPositionBlockedByTopology(position, ref spawningMonumentInfo)) return false; if (IsAnyColliderBlockSpawn(position, ref spawningMonumentInfo, ignorePlayers)) return false; return true; } } static class GroundSpawner { internal static void SpawnInRandomPosition(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { ServerMgr.Instance.StartCoroutine(SpawnGroundLocationCoroutine(spawningMonumentInfo, initiator)); } static IEnumerator SpawnGroundLocationCoroutine(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { int counter = 25000; BuildingSite site = null; while (counter-- > 0) { Vector3 position = PositionDefiner.GetRandomMapPoint(); if (IsPositionSuitable(ref position, ref spawningMonumentInfo, false, false)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); site = SpawnSite(spawningMonumentInfo, position, rotation, false); break; } else if (counter % 10 == 0) yield return CoroutineEx.waitForEndOfFrame; } if (site == null) NotifyManagerLite.PrintInfoMessage(initiator, "CantFindPosition_Exeption", spawningMonumentInfo.config.presetName); } internal static bool IsPositionSuitable(ref Vector3 position, ref SiteSpawnInfo spawningMonumentInfo, bool ignorePlayers, bool isPlayerSummoned) { float mapHeigth = TerrainMeta.HeightMap.GetHeight(position); if (position.y < -0.5f) return false; if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, ref spawningMonumentInfo)) return false; if (!isPlayerSummoned && !IsBiomeSuitable(position, ref spawningMonumentInfo)) return false; if (IsPositionBlockedByTopology(position, ref spawningMonumentInfo)) return false; if (IsAnyColliderBlockSpawn(position, ref spawningMonumentInfo, ignorePlayers)) return false; if (isPlayerSummoned) { bool isSuitable = false; Vector3 newPosition = position; for (int i = -10; i <= 10; i++) { if (isSuitable) break; newPosition = position + Vector3.up * i / 2; if (IsPositionBlockByHeigthInRadius(newPosition, spawningMonumentInfo.config.internalRadius, spawningMonumentInfo.config.maxUpDeltaHeigh, spawningMonumentInfo.config.maxDownDeltaHeigh)) continue; if (IsPositionBlockByHeigthInRadius(newPosition, spawningMonumentInfo.config.internalRadius * 0.5f, spawningMonumentInfo.config.maxUpDeltaHeigh, spawningMonumentInfo.config.maxDownDeltaHeigh)) continue; isSuitable = true; } if (isSuitable) position = newPosition; else { spawningMonumentInfo.reasonOfFail = "UnsuitableLandscape_BlockSpawn"; return false; } } else { if (IsPositionBlockByHeigthInRadius(position, spawningMonumentInfo.config.internalRadius, spawningMonumentInfo.config.maxUpDeltaHeigh, spawningMonumentInfo.config.maxDownDeltaHeigh)) { spawningMonumentInfo.reasonOfFail = "UnsuitableLandscape_BlockSpawn"; return false; } if (IsPositionBlockByHeigthInRadius(position, spawningMonumentInfo.config.internalRadius * 0.5f, spawningMonumentInfo.config.maxUpDeltaHeigh, spawningMonumentInfo.config.maxDownDeltaHeigh)) { spawningMonumentInfo.reasonOfFail = "UnsuitableLandscape_BlockSpawn"; return false; } } return true; } internal static BuildingSite TrySpawnLocation(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo) { if (IsPositionSuitable(ref position, ref spawningMonumentInfo, false, true)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); return SpawnSite(spawningMonumentInfo, position, rotation, true); } return null; } } static class SkySpawner { internal static void SpawnInRandomPosition(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { ServerMgr.Instance.StartCoroutine(SpawnSkyLocationCoroutine(spawningMonumentInfo, initiator)); } static IEnumerator SpawnSkyLocationCoroutine(SiteSpawnInfo spawningMonumentInfo, BasePlayer initiator) { int counter = 25000; BuildingSite site = null; while (counter-- > 0) { Vector3 position = PositionDefiner.GetRandomMapPoint(); if (IsPositionSuitable(ref position, ref spawningMonumentInfo, false, false)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); site = SpawnSite(spawningMonumentInfo, position, rotation, false); break; } else if (counter % 10 == 0) yield return CoroutineEx.waitForEndOfFrame; } if (site == null) NotifyManagerLite.PrintInfoMessage(initiator, "CantFindPosition_Exeption", spawningMonumentInfo.config.presetName); } internal static bool IsPositionSuitable(ref Vector3 position, ref SiteSpawnInfo spawningMonumentInfo, bool ignorePlayers, bool isPlayerSummoned) { Vector3 groundPosition = PositionDefiner.GetGroundPosition(position); position.y = UnityEngine.Random.Range(ins._config.skyTypeConfig.minSpawnHeight, ins._config.skyTypeConfig.maxSpawnHeight); float distanceToGround = position.y - groundPosition.y; if (distanceToGround < ins._config.skyTypeConfig.minSpawnHeight / 2f) { spawningMonumentInfo.reasonOfFail = "UnsuitableLandscape_BlockSpawn"; return false; } if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, ref spawningMonumentInfo)) return false; if (!IsPositionInMapBounds(position, spawningMonumentInfo.config)) { spawningMonumentInfo.reasonOfFail = "FarShore_BlockSpawn"; return false; } if (IsAnyColliderBlockSpawn(position, ref spawningMonumentInfo, ignorePlayers)) return false; return true; } internal static BuildingSite TrySpawnMonument(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo) { if (IsPositionSuitable(ref position, ref spawningMonumentInfo, false, true)) { Quaternion rotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0); return SpawnSite(spawningMonumentInfo, position, rotation, true); } return null; } } static bool IsPositionInMapBounds(Vector3 position, BaseSiteConfig siteConfig) { if (Mathf.Abs(position.x) + siteConfig.externalRadius > World.Size / 2) return false; if (Mathf.Abs(position.z) + siteConfig.externalRadius > World.Size / 2) return false; return true; } static bool IsBiomeSuitable(Vector3 postition, ref SiteSpawnInfo spawningMonumentInfo) { if (spawningMonumentInfo.config.biomes.Count == 0) return true; TerrainBiome.Enum biome = (TerrainBiome.Enum)TerrainMeta.BiomeMap.GetBiomeMaxType(postition); bool isBiomeSuitable = spawningMonumentInfo.config.biomes.Contains(biome.ToString()); if (!isBiomeSuitable) { spawningMonumentInfo.reasonOfFail = "WrongBiome_BlockSpawn"; return false; } return true; } static bool IsPositionBlockByHeigthInRadius(Vector3 postition, float radius, float maxUpDelta, float maxDownDelta, int angleStep = 15) { for (int angle = 0; angle < 360; angle += angleStep) { float radian = 2f * Mathf.PI * angle / 360; float x = postition.x + radius * Mathf.Cos(radian); float z = postition.z + radius * Mathf.Sin(radian); Vector3 positionInRadius = PositionDefiner.GetGroundPosition(new Vector3(x, postition.y, z)); if (positionInRadius.y < 0) continue; float delta = positionInRadius.y - postition.y; if (delta > 0 && delta > maxUpDelta) return true; if (delta < 0 && -delta > maxDownDelta) return true; } return false; } static bool IsAnyColliderBlockSpawn(Vector3 position, ref SiteSpawnInfo spawningMonumentInfo, bool ignorePlayers = false) { HashSet blockedColliders = new HashSet { "rock_formation_large", "rock_formation_huge", "iceberg", "prevent_building", "preventbuilding", "prevent building" }; foreach (Collider collider in UnityEngine.Physics.OverlapSphere(position, spawningMonumentInfo.config.internalRadius)) { if (collider.name.Contains("heatSource")) continue; BaseEntity entity = collider.ToBaseEntity(); if (entity == null) { string colliderLowerName = collider.name.ToLower(); if (spawningMonumentInfo.type != LocationType.Prefab && blockedColliders.Any(x => colliderLowerName.Contains(x))) { spawningMonumentInfo.reasonOfFail = "Object_BlockSpawn"; return true; } continue; } if (ignorePlayers && entity is BaseBoat && (spawningMonumentInfo.type == LocationType.Water || spawningMonumentInfo.type == LocationType.Shore)) continue; if (entity is BaseVehicle && entity is not TrainCar) { spawningMonumentInfo.reasonOfFail = "Object_BlockSpawn"; return true; } if (entity is JunkPile or DiveSite or LootContainer or OreResourceEntity or BasePortal) spawningMonumentInfo.entitiesForDestroy.Add(entity); else if (ins._config.mainConfig.killTrees && entity is TreeEntity) spawningMonumentInfo.entitiesForDestroy.Add(entity); } foreach (Collider collider in UnityEngine.Physics.OverlapSphere(position, spawningMonumentInfo.config.externalRadius)) { if (collider.name.Contains("heatSource")) continue; if (collider.name.Contains("Safe")) { spawningMonumentInfo.reasonOfFail = "Object_BlockSpawn"; return true; } BaseEntity entity = collider.ToBaseEntity(); if (entity == null) continue; if (!ignorePlayers) { BasePlayer basePlayer = entity as BasePlayer; if (basePlayer.IsRealPlayer()) { spawningMonumentInfo.reasonOfFail = "Player_BlockSpawn"; return true; } } if (entity.GetBuildingPrivilege() != null) { spawningMonumentInfo.reasonOfFail = "Object_BlockSpawn"; return true; } if (entity is BuildingBlock or SimpleBuildingBlock) { spawningMonumentInfo.reasonOfFail = "Object_BlockSpawn"; return true; } } return false; } static bool IsPositionBlockedByTopology(Vector3 postition, ref SiteSpawnInfo spawningMonumentInfo, int angleStep = 15) { if (TopologyChecker.IsBlockedTopology(postition)) { spawningMonumentInfo.reasonOfFail = "WrongPlace_BlockSpawn"; return true; } for (int angle = 0; angle < 360; angle += angleStep) { float radian = 2f * Mathf.PI * angle / 360; float x = postition.x + spawningMonumentInfo.config.externalRadius * Mathf.Cos(radian); float z = postition.z + spawningMonumentInfo.config.externalRadius * Mathf.Sin(radian); Vector3 positionInRadius = PositionDefiner.GetGroundPosition(new Vector3(x, postition.y, z)); if (TopologyChecker.IsBlockedTopology(positionInRadius)) { spawningMonumentInfo.reasonOfFail = "WrongPlace_BlockSpawn"; return true; } if (spawningMonumentInfo.type == LocationType.Water && !TopologyChecker.IsOceanTopology(positionInRadius)) { spawningMonumentInfo.reasonOfFail = "WrongPlace_BlockSpawn"; return true; } } return false; } } class SiteSpawnInfo { internal BaseSiteConfig config; internal BuildingSiteData data; internal LocationType type; internal HashSet entitiesForDestroy = new HashSet(); internal string reasonOfFail = string.Empty; internal SiteSpawnInfo(BaseSiteConfig config, BuildingSiteData data, LocationType type) { this.config = config; this.data = data; this.type = type; } internal static SiteSpawnInfo GetSiteInfo(string presetName) { LocationType monumentType; BaseSiteConfig siteConfig = GetSiteConfig(presetName, out monumentType); if (siteConfig == null) return null; BuildingSiteData data; if (!ins.siteCustomizationDatas.TryGetValue(siteConfig.dataFileName, out data)) return null; return new SiteSpawnInfo(siteConfig, data, monumentType); } internal static SiteSpawnInfo GetSpawningMonumentInfo(ulong skinID) { LocationType monumentType; BaseSiteConfig config = GetSiteConfig(skinID, out monumentType); if (config == null) return null; BuildingSiteData data; if (!ins.siteCustomizationDatas.TryGetValue(config.dataFileName, out data)) return null; return new SiteSpawnInfo(config, data, monumentType); } internal static BaseSiteConfig GetSiteConfig(string presetName, out LocationType monumentType) { BaseSiteConfig config; config = GetConfigByPreset(ins._config.groundTypeConfig.sites, presetName); if (config != null) { monumentType = LocationType.Ground; return config; } config = GetConfigByPreset(ins._config.waterTypeConfig.sites, presetName); if (config != null) { monumentType = LocationType.Water; return config; } config = GetConfigByPreset(ins._config.skyTypeConfig.sites, presetName); if (config != null) { monumentType = LocationType.Sky; return config; } monumentType = LocationType.None; return null; } internal static BaseSiteConfig GetSiteConfig(ulong skinID, out LocationType monumentType) { BaseSiteConfig config; config = GetConfigBySkin(ins._config.groundTypeConfig.sites, skinID); if (config != null) { monumentType = LocationType.Ground; return config; } config = GetConfigBySkin(ins._config.waterTypeConfig.sites, skinID); if (config != null) { monumentType = LocationType.Water; return config; } config = GetConfigBySkin(ins._config.skyTypeConfig.sites, skinID); if (config != null) { monumentType = LocationType.Sky; return config; } monumentType = LocationType.None; return null; } static BaseSiteConfig GetConfigByPreset(HashSet sites, string presetName) { return sites.FirstOrDefault(x => x.presetName == presetName); } static BaseSiteConfig GetConfigBySkin(HashSet monuments, ulong skinID) { return monuments.FirstOrDefault(x => x.summonConfig != null && x.summonConfig.skin == skinID); } } enum LocationType { None, Prefab, Water, River, Road, Shore, Ground, Sky } static class TopologyChecker { const int oceanTopologies = (int)(TerrainTopology.Enum.Ocean | TerrainTopology.Enum.Oceanside); const int blockedTopologies = (int)(TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside | TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside | TerrainTopology.Enum.Monument | TerrainTopology.Enum.Building); internal static bool IsPositionHaveTopology(Vector3 position, int topology) { int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position); if ((pointTopologies & topology) != 0) return true; return false; } internal static bool IsBlockedTopology(Vector3 position, int topologies = blockedTopologies) { int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position); if ((pointTopologies & topologies) != 0) return true; return false; } internal static bool IsShoreTopology(Vector3 position) { int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position); if ((pointTopologies & (int)TerrainTopology.Enum.Oceanside) != 0) return true; return false; } internal static bool IsOceanTopology(Vector3 position) { int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position); if ((pointTopologies & oceanTopologies) != 0) return true; return false; } } class BuildingSite : FacepunchBehaviour { static HashSet buildingSites = new HashSet(); internal BaseSiteConfig siteConfig; BaseEntity mainEntity; BuildingSiteData siteData; MapMarker mapMarker; LocationType locationType; HashSet shoudDestroyAfterUnload = new HashSet(); internal static void Initialize() { LoadSites(); if (buildingSites.Count == 0) LocationAutoSpawner.StartAutoSpawn(); } static void LoadSites() { HashSet doorClosers = BaseNetworkable.serverEntities.OfType(); foreach (DoorCloser doorCloser in doorClosers) { if (doorCloser == null || doorCloser.net == null) continue; BaseSiteConfig siteConfig = GetSiteConfigBySkinId(doorCloser.skinID); if (siteConfig == null) continue; BuildingSite buildingSite = TryAttachBuildingSiteClass(doorCloser, siteConfig.presetName, false); } } internal static BaseSiteConfig GetSiteConfigByPresetName(string presetName) { BaseSiteConfig result = null; result = ins._config.groundTypeConfig.sites.FirstOrDefault(x => x.presetName == presetName); if (result == null) result = ins._config.waterTypeConfig.sites.FirstOrDefault(x => x.presetName == presetName); if (result == null) result = ins._config.skyTypeConfig.sites.FirstOrDefault(x => x.presetName == presetName); return result; } internal static BaseSiteConfig GetSiteConfigBySkinId(ulong skinID) { BaseSiteConfig result = null; result = ins._config.groundTypeConfig.sites.FirstOrDefault(x => x.summonConfig.skin == skinID); if (result == null) result = ins._config.waterTypeConfig.sites.FirstOrDefault(x => x.summonConfig.skin == skinID); if (result == null) result = ins._config.skyTypeConfig.sites.FirstOrDefault(x => x.summonConfig.skin == skinID); return result; } internal static BuildingSite GetSiteByEntity(BaseEntity entity) { return buildingSites.FirstOrDefault(x => x != null && x.mainEntity.IsExists() && x.IsSiteEntity(entity)); } internal static BuildingSite GetSiteByBuildingPrivilege(BuildingPrivlidge buildingPrivlidge) { foreach (BuildingSite buildingSite in buildingSites) { if (buildingSite == null || buildingSite.mainEntity == null) return null; BuildingPrivlidge sitePrivilege = buildingSite.mainEntity.GetBuildingPrivilege(); if (sitePrivilege != null && sitePrivilege.net != null && sitePrivilege.net.ID.Value == buildingPrivlidge.net.ID.Value) return buildingSite; foreach (BaseEntity childEntity in buildingSite.mainEntity.children) { if (!childEntity.ShortPrefabName.Contains("admin")) continue; sitePrivilege = childEntity.GetBuildingPrivilege(); if (sitePrivilege != null && sitePrivilege.net != null && sitePrivilege.net.ID.Value == buildingPrivlidge.net.ID.Value) return buildingSite; } } return null; } internal static BuildingSite GetClosestSite(Vector3 position, LocationType locationType, out float minDistance) { minDistance = float.MaxValue; BuildingSite result = null; foreach (BuildingSite buildingSite in buildingSites) { if (buildingSite == null || buildingSite.locationType != locationType) continue; Vector3 offset = buildingSite.siteData.offset.ToVector3(); float distance = Vector3.Distance(position, buildingSite.mainEntity.transform.position + offset); if (distance < minDistance) { minDistance = distance; result = buildingSite; } } return result; } internal static HashSet GetPlayersSites(ulong userID) { return buildingSites.Where(x => x != null && x.mainEntity.IsExists() && x.mainEntity.OwnerID == userID); } internal static bool IsSpawnPositionBlockByOtherSite(Vector3 position, ref SiteSpawnInfo siteSpawnInfo) { position.y = 0; foreach (BuildingSite site in buildingSites) { if (site == null || !site.mainEntity.IsExists()) continue; Vector3 siteGroundPosition = new Vector3(site.mainEntity.transform.position.x, 0, site.mainEntity.transform.position.z); float distance = Vector3.Distance(position, siteGroundPosition); if (distance < (siteSpawnInfo.config.externalRadius + site.siteConfig.externalRadius) * 1.2f) { siteSpawnInfo.reasonOfFail = "CloseSite_BlockSpawn"; return true; } } return false; } internal static BuildingSite SpawnSite(string presetName, Vector3 position, Quaternion rotation) { BaseSiteConfig siteConfig = GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName); return null; } BaseEntity mainEntity = BuildManager.SpawnRegularEntity("assets/prefabs/misc/doorcloser/doorcloser.prefab", position, rotation, siteConfig.summonConfig.skin, true); BuildingSite buildingSite = TryAttachBuildingSiteClass(mainEntity, presetName, true); if (buildingSite == null) mainEntity.Kill(); return buildingSite; } internal static BuildingSite TryAttachBuildingSiteClass(BaseEntity mainEntity, string presetName, bool firstSpawn) { BaseSiteConfig siteConfig = GetSiteConfigByPresetName(presetName); if (siteConfig == null) { NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName); return null; } BuildingSiteData siteData; ins.siteCustomizationDatas.TryGetValue(siteConfig.dataFileName, out siteData); if (siteData == null) { NotifyManagerLite.PrintError(null, "DataFileNotFound_Exeption", siteConfig.dataFileName); return null; } BuildingSite buildingSite = mainEntity.gameObject.AddComponent(); buildingSites.Add(buildingSite); buildingSite.BuildSite(mainEntity, siteConfig, siteData, firstSpawn); return buildingSite; } void BuildSite(BaseEntity mainEntity, BaseSiteConfig siteConfig, BuildingSiteData siteData, bool firstSpawn) { this.mainEntity = mainEntity; this.siteConfig = siteConfig; this.siteData = siteData; Vector3 offset = siteData.offset.ToVector3(); foreach (EntityData entityData in siteData.decorEntities) { Vector3 localPosition = entityData.position.ToVector3() + offset; Vector3 localRotation = entityData.rotation.ToVector3(); if (!firstSpawn && mainEntity.children.Any(x => x != null && x.PrefabName == entityData.prefabName && x.transform.localPosition == localPosition)) continue; BaseEntity entity = BuildManager.SpawnChildEntity(mainEntity, entityData.prefabName, localPosition, localRotation, isDecor: true, enableSaving: false); if (entity.ShortPrefabName == "coaling_tower_fuel_storage.entity" || entity.ShortPrefabName == "mailbox.deployed") entity.SetFlag(BaseEntity.Flags.Busy, true); if (entity.ShortPrefabName == "hotairballoon") { entity.SetFlag(BaseEntity.Flags.On, true); entity.SetFlag(BaseEntity.Flags.Reserved11, true); entity.SetFlag(BaseEntity.Flags.Reserved12, true); entity.SetFlag(BaseEntity.Flags.Reserved13, true); entity.SendNetworkUpdate(); } } foreach (BuildingBlockData buildingBlockData in siteData.buildingBlocks) { Vector3 localPosition = buildingBlockData.position.ToVector3() + offset; Vector3 localRotation = buildingBlockData.rotation.ToVector3(); BuildingBlock thisBuildingBlock = mainEntity.children.FirstOrDefault(x => x != null && x.PrefabName == buildingBlockData.prefabName && x.transform.localPosition == localPosition && x.transform.localEulerAngles == localRotation) as BuildingBlock; if (thisBuildingBlock != null) { thisBuildingBlock.grounded = true; continue; } BuildingBlock buildingBlock = BuildManager.SpawnChildBuildingBlock(buildingBlockData.prefabName, buildingBlockData.grade, buildingBlockData.color, buildingBlockData.skin, localPosition, localRotation, mainEntity); buildingBlock.grounded = true; } foreach (EntityData regularEntityData in siteData.regularEntities) { Vector3 localPosition = regularEntityData.position.ToVector3() + offset; Vector3 localRotation = regularEntityData.rotation.ToVector3(); if (!firstSpawn && mainEntity.children.Any(x => x != null && x.PrefabName == regularEntityData.prefabName && Vector3.Distance(x.transform.localPosition, localPosition) < 0.1f)) continue; BaseEntity baseEntity = BuildManager.SpawnChildEntity(mainEntity, regularEntityData.prefabName, localPosition, localRotation, isDecor: false, enableSaving: true); if (baseEntity is PercentFullStorageContainer) baseEntity.SetFlag(BaseEntity.Flags.Busy, true); HotAirBalloon hotAirBalloon = baseEntity as HotAirBalloon; if (hotAirBalloon != null) { Rigidbody rigidbody = hotAirBalloon.myRigidbody; rigidbody.isKinematic = true; rigidbody.freezeRotation = true; hotAirBalloon.inflationLevel = 1; hotAirBalloon.enabled = false; hotAirBalloon.SendNetworkUpdate(); hotAirBalloon.enableSaving = false; } BaseLadder baseLadder = baseEntity as BaseLadder; if (baseLadder != null) baseLadder.baseProtection = ins.protection; } foreach (BoxColliderData colliderData in siteData.preventBuildingColliders) CreateBoxCollider(colliderData, offset, 29); if (siteData.wireDatas != null) { foreach (WireData wireData in siteData.wireDatas) { Vector3 localStartPosition = wireData.startPosition.ToVector3() + offset; Vector3 localEndPosition = wireData.endPosition.ToVector3() + offset; Vector3 globalStartPosition = PositionDefiner.GetGlobalPosition(mainEntity.transform, localStartPosition); Vector3 globalEndPosition = PositionDefiner.GetGlobalPosition(mainEntity.transform, localEndPosition); CustomDoorManipulator doorManipulator = BuildManager.SpawnChildEntity(mainEntity, "assets/prefabs/deployable/playerioents/doormanipulators/doorcontroller.deployed.prefab", localStartPosition, Vector3.zero, isDecor: false, enableSaving: false) as CustomDoorManipulator; IOEntity.IOSlot ioOutput = doorManipulator.outputs[0]; ioOutput.connectedTo.entityRef.uid = doorManipulator.net.ID; ioOutput.connectedTo = new IOEntity.IORef(); ioOutput.connectedTo.Set(doorManipulator); ioOutput.connectedToSlot = 0; ioOutput.linePoints = new List { new Vector3(0, 0, 0), PositionDefiner.GetLocalPosition(doorManipulator.transform, globalEndPosition) }.ToArray(); ioOutput.connectedTo.Init(); doorManipulator.MarkDirtyForceUpdateOutputs(); doorManipulator.SendNetworkUpdate(); } } if (!mainEntity.children.Any(x => x != null && x.GetBuildingPrivilege() != null)) mapMarker = MapMarker.CreateMarker(mainEntity.transform.position); if (ins._config.groundTypeConfig.sites.Any(x => x.presetName == siteConfig.presetName)) locationType = LocationType.Ground; else if (ins._config.waterTypeConfig.sites.Any(x => x.presetName == siteConfig.presetName)) locationType = LocationType.Water; else if (ins._config.skyTypeConfig.sites.Any(x => x.presetName == siteConfig.presetName)) locationType = LocationType.Sky; } void CreateBoxCollider(BoxColliderData colliderData, Vector3 offset, int layer) { Vector3 size = colliderData.size.ToVector3(); Vector3 localPosition = colliderData.position.ToVector3() + offset; Vector3 localRotation = colliderData.rotation.ToVector3(); GameObject gameObject = new GameObject(layer == 29 ? "PreventBuildingCollider" : "PreventMovementCollider"); gameObject.transform.localPosition = localPosition; gameObject.transform.localEulerAngles = localRotation; gameObject.transform.SetParent(mainEntity.transform, false); gameObject.layer = layer; BoxCollider boxCollider = gameObject.AddComponent(); boxCollider.size = size; boxCollider.center = Vector3.zero; shoudDestroyAfterUnload.Add(gameObject); } bool IsSiteEntity(BaseEntity entity) { if (entity.net != null && entity.net.ID.Value == mainEntity.net.ID.Value) return true; BaseEntity parentEntity = entity.GetParentEntity(); if (parentEntity == null || parentEntity.net == null) return false; if (parentEntity.net.ID.Value == mainEntity.net.ID.Value) return true; return false; } internal void SetOwner(ulong userID) { mainEntity.OwnerID = userID; } internal static void UnloadSites() { foreach (BuildingSite buildingSite in buildingSites) if (buildingSite != null) buildingSite.UnloadSite(); } internal void UnloadSite() { DeleteMapMarker(); foreach (GameObject gameObject in shoudDestroyAfterUnload) if (gameObject != null) UnityEngine.GameObject.Destroy(gameObject); } internal void DeleteMapMarker() { if (mapMarker != null) mapMarker.Delete(); } internal static void KillAllSites() { foreach (BuildingSite buildingSites in buildingSites) if (buildingSites != null && buildingSites.mainEntity.IsExists()) buildingSites.KillSite(); } internal void KillSite() { if (mainEntity.IsExists()) mainEntity.Kill(); } void OnDestroy() { UnloadSite(); } } class MapMarker : FacepunchBehaviour { MapMarkerGenericRadius radiusMarker; VendingMachineMapMarker vendingMarker; Coroutine updateCounter; internal static MapMarker CreateMarker(Vector3 position) { if (!ins._config.markerConfig.enable) return null; GameObject gameObject = new GameObject(); gameObject.transform.position = position; gameObject.layer = (int)Rust.Layer.Reserved1; MapMarker mapMarker = gameObject.AddComponent(); mapMarker.Init(); return mapMarker; } void Init() { CreateRadiusMarker(); CreateVendingMarker(); updateCounter = ServerMgr.Instance.StartCoroutine(MarkerUpdateCounter()); } void CreateRadiusMarker() { if (!ins._config.markerConfig.useRingMarker) return; radiusMarker = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", this.gameObject.transform.position) as MapMarkerGenericRadius; radiusMarker.enableSaving = false; radiusMarker.Spawn(); radiusMarker.radius = ins._config.markerConfig.radius; radiusMarker.alpha = ins._config.markerConfig.alpha; radiusMarker.color1 = new Color(ins._config.markerConfig.color1.r, ins._config.markerConfig.color1.g, ins._config.markerConfig.color1.b); radiusMarker.color2 = new Color(ins._config.markerConfig.color2.r, ins._config.markerConfig.color2.g, ins._config.markerConfig.color2.b); } void CreateVendingMarker() { if (!ins._config.markerConfig.useShopMarker) return; vendingMarker = GameManager.server.CreateEntity("assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab", this.gameObject.transform.position) as VendingMachineMapMarker; vendingMarker.enableSaving = false; vendingMarker.Spawn(); vendingMarker.markerShopName = $"{ins._config.markerConfig.displayedName}"; vendingMarker.SetFlag(BaseEntity.Flags.Busy, false); vendingMarker.SendNetworkUpdate(); } IEnumerator MarkerUpdateCounter() { while (true) { UpdateVendingMarker(); UpdateRadiusMarker(); yield return CoroutineEx.waitForSeconds(1f); } } void UpdateRadiusMarker() { if (!radiusMarker.IsExists()) return; radiusMarker.SendUpdate(); radiusMarker.SendNetworkUpdate(); } void UpdateVendingMarker() { if (!vendingMarker.IsExists()) return; vendingMarker.SetFlag(BaseEntity.Flags.Busy, true); vendingMarker.SendNetworkUpdate(); } internal void Delete() { if (radiusMarker.IsExists()) radiusMarker.Kill(); if (vendingMarker.IsExists()) vendingMarker.Kill(); if (updateCounter != null) ServerMgr.Instance.StopCoroutine(updateCounter); Destroy(this.gameObject); } } class Ch47Killer : FacepunchBehaviour { static HashSet chKillers = new HashSet(); CH47HelicopterAIController ch47; internal static void AttachClass(CH47HelicopterAIController ch47) { Ch47Killer ch47Killer = ch47.gameObject.AddComponent(); ch47Killer.Init(ch47); chKillers.Add(ch47Killer); } internal static void Unload() { foreach (Ch47Killer ch47Killer in chKillers) if (ch47Killer != null) UnityEngine.GameObject.Destroy(ch47Killer); } void Init(CH47HelicopterAIController ch47) { this.ch47 = ch47; } void OnCollisionEnter(Collision collision) { if (collision == null || collision.collider == null) return; BaseEntity entity = collision.GetEntity(); if (entity == null) return; BuildingSite buildingSite = BuildingSite.GetSiteByEntity(entity); if (buildingSite == null) return; ins.NextTick(() => { if (ch47.IsExists()) { ch47.DismountAllPlayers(); ch47.Kill(BaseNetworkable.DestroyMode.Gib); } }); } } static class BuildManager { internal static BuildingBlock SpawnChildBuildingBlock(BuildingBlockData buildingBlockData, BaseEntity parentEntity) { BuildingBlock buildingBlock = CreateEntity(buildingBlockData.prefabName, parentEntity.transform.position, Quaternion.identity, 0, true) as BuildingBlock; SetParent(parentEntity, buildingBlock, buildingBlockData.position.ToVector3(), buildingBlockData.rotation.ToVector3()); buildingBlock.AttachToBuilding(BuildingManager.server.NewBuildingID()); buildingBlock.grounded = true; buildingBlock.cachedStability = 1; buildingBlock.Spawn(); BuildingManager.server.decayEntities.Remove(buildingBlock); BuildingGrade.Enum buildingGrade = (BuildingGrade.Enum)buildingBlockData.grade; buildingBlock.ChangeGradeAndSkin(buildingGrade, buildingBlockData.skin); if (buildingBlockData.color != 0) buildingBlock.SetCustomColour(buildingBlockData.color); return buildingBlock; } internal static BuildingBlock SpawnChildBuildingBlock(string prefabName, int grade, uint color, ulong skin, Vector3 position, Vector3 rotation, BaseEntity parentEntity) { BuildingBlock buildingBlock = CreateEntity(prefabName, parentEntity.transform.position, Quaternion.identity, 0, true) as BuildingBlock; SetParent(parentEntity, buildingBlock, position, rotation); buildingBlock.AttachToBuilding(BuildingManager.server.NewBuildingID()); buildingBlock.grounded = true; buildingBlock.cachedStability = 1; buildingBlock.Spawn(); BuildingManager.server.decayEntities.Remove(buildingBlock); BuildingGrade.Enum buildingGrade = (BuildingGrade.Enum)grade; buildingBlock.ChangeGradeAndSkin(buildingGrade, skin); if (color != 0) buildingBlock.SetCustomColour(color); return buildingBlock; } internal static void HideWirePoints(IOEntity entity) { entity.inputs = System.Array.Empty(); foreach (IOEntity.IOSlot slot in entity.outputs) slot.type = IOEntity.IOType.Generic; } internal static void UpdateMeshColliders(BaseEntity entity) { MeshCollider[] meshColliders = entity.GetComponentsInChildren(); for (int i = 0; i < meshColliders.Length; i++) { MeshCollider meshCollider = meshColliders[i]; } } internal static BaseEntity SpawnRegularEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId = 0, bool enableSaving = false) { BaseEntity entity = CreateEntity(prefabName, position, rotation, skinId, enableSaving); entity.Spawn(); return entity; } internal static BaseEntity SpawnStaticEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId = 0) { BaseEntity entity = CreateEntity(prefabName, position, rotation, skinId, false); DestroyUnnessesaryComponents(entity); StabilityEntity stabilityEntity = entity as StabilityEntity; if (stabilityEntity != null) stabilityEntity.grounded = true; BaseCombatEntity baseCombatEntity = entity as BaseCombatEntity; if (baseCombatEntity != null) baseCombatEntity.pickup.enabled = false; entity.Spawn(); return entity; } internal static BaseEntity SpawnChildEntity(BaseEntity parrentEntity, string prefabName, Vector3 localPosition, Vector3 localRotation, ulong skinId = 0, bool isDecor = true, bool enableSaving = false) { BaseEntity entity = isDecor ? CreateDecorEntity(prefabName, parrentEntity.transform.position, Quaternion.identity, skinId) : CreateEntity(prefabName, parrentEntity.transform.position, Quaternion.identity, skinId, enableSaving); SetParent(parrentEntity, entity, localPosition, localRotation); DestroyUnnessesaryComponents(entity); if (isDecor) DestroyDecorComponents(entity); entity.Spawn(); UpdateMeshColliders(entity); return entity; } internal static void UpdateEntityMaxHealth(BaseCombatEntity baseCombatEntity, float maxHealth) { baseCombatEntity.startHealth = maxHealth; baseCombatEntity.InitializeHealth(maxHealth, maxHealth); } internal static BaseEntity CreateEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId, bool enableSaving) { BaseEntity entity = GameManager.server.CreateEntity(prefabName, position, rotation); entity.enableSaving = enableSaving; entity.skinID = skinId; return entity; } internal static BaseEntity CreateDecorEntity(string prefabName, Vector3 position, Quaternion rotation, ulong skinId = 0, bool enableSaving = false) { BaseEntity entity = CreateEntity(prefabName, position, rotation, skinId, enableSaving); BaseEntity trueBaseEntity = entity.gameObject.AddComponent(); CopySerializableFields(entity, trueBaseEntity); UnityEngine.Object.DestroyImmediate(entity, true); entity.SetFlag(BaseEntity.Flags.Busy, true); entity.SetFlag(BaseEntity.Flags.Locked, true); return trueBaseEntity; } internal static void SetParent(BaseEntity parrentEntity, BaseEntity childEntity, Vector3 localPosition, Vector3 localRotation) { childEntity.transform.localPosition = localPosition; childEntity.transform.localEulerAngles = localRotation; childEntity.SetParent(parrentEntity); } static void DestroyDecorComponents(BaseEntity entity) { DestroyEntityConponent(entity); DestroyEntityConponents(entity); Component[] components = entity.GetComponentsInChildren(); for (int i = 0; i < components.Length; i++) { Component component = components[i]; EntityCollisionMessage entityCollisionMessage = component as EntityCollisionMessage; if (entityCollisionMessage != null || (component != null && component.name != entity.PrefabName)) { Transform transform = component as Transform; if (transform != null) continue; Collider collider = component as Collider; if (collider != null && !collider.isTrigger && collider.gameObject.layer != 29) continue; if (component is Model) continue; UnityEngine.GameObject.DestroyImmediate(component as UnityEngine.Object); } } } static void DestroyUnnessesaryComponents(BaseEntity entity) { DestroyEntityConponent(entity); DestroyEntityConponent(entity); DestroyEntityConponent(entity); if (entity is not HotAirBalloon) { DestroyEntityConponent(entity); } } internal static void DestroyEntityConponent(BaseEntity entity) where TypeForDestroy : UnityEngine.Object { if (entity == null) return; TypeForDestroy component = entity.GetComponent(); if (component != null) UnityEngine.GameObject.DestroyImmediate(component); } internal static void DestroyEntityConponents(BaseEntity entity) where TypeForDestroy : UnityEngine.Object { if (entity == null) return; TypeForDestroy[] components = entity.GetComponentsInChildren(); for (int i = 0; i < components.Length; i++) { TypeForDestroy component = components[i]; if (component != null) UnityEngine.GameObject.DestroyImmediate(component); } } internal static void CopySerializableFields(T src, T dst) { FieldInfo[] srcFields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in srcFields) { object value = field.GetValue(src); field.SetValue(dst, value); } } } static class MapSaver { static Vector3 loactionCenterPosition = new Vector3(0, 150, 0); static float radiusForSaving = 70; static Dictionary colliderToPrefabs = new Dictionary { ["wall.frame.fence"] = "assets/prefabs/building/wall.frame.fence/wall.frame.fence.prefab", ["glass_collider"] = "assets/prefabs/building/wall.window.reinforcedglass/wall.window.glass.reinforced.prefab", }; internal static void SaveMap(string dataFileName) { BuildingSiteData buildingSiteData = new BuildingSiteData(); SaveAllEntitiesInRadius(ref buildingSiteData); ins.SaveDataFile(buildingSiteData, dataFileName); } static void SaveAllEntitiesInRadius(ref BuildingSiteData buildingSiteData) { buildingSiteData.offset = "(0, 0, 0)"; buildingSiteData.decorEntities = new HashSet(); buildingSiteData.regularEntities = new HashSet(); buildingSiteData.buildingBlocks = new HashSet(); buildingSiteData.preventBuildingColliders = new HashSet(); List colliders = Physics.OverlapSphere(loactionCenterPosition, radiusForSaving).Where(x => true).OrderBy(x => x.transform.position.z); foreach (Collider collider in colliders) { if (collider.name.Contains("prevent_building")) { BoxCollider boxCollider = collider.gameObject.GetComponentInChildren(); if (boxCollider == null) continue; BoxColliderData preventBuildingColliderData = new BoxColliderData(); preventBuildingColliderData.position = GetPosition(boxCollider.transform); preventBuildingColliderData.rotation = GetRotation(boxCollider.transform); preventBuildingColliderData.size = $"({boxCollider.transform.localScale.x}, {boxCollider.transform.localScale.y}, {boxCollider.transform.localScale.z})"; if (preventBuildingColliderData != null && !buildingSiteData.preventBuildingColliders.Any(x => x.position == preventBuildingColliderData.position && x.rotation == preventBuildingColliderData.rotation && x.size == preventBuildingColliderData.size)) buildingSiteData.preventBuildingColliders.Add(preventBuildingColliderData); continue; } if (collider.name.Contains("building core")) { BuildingBlockData buildingBlockData = new BuildingBlockData(); buildingBlockData.prefabName = GetBuildingBlockPrefabName(collider.name); buildingBlockData.grade = 3; buildingBlockData.position = GetPosition(collider.transform); buildingBlockData.rotation = GetRotation(collider.transform); if (buildingBlockData != null && !buildingSiteData.buildingBlocks.Any(x => x.prefabName == buildingBlockData.prefabName && x.position == buildingBlockData.position && x.rotation == buildingBlockData.rotation)) buildingSiteData.buildingBlocks.Add(buildingBlockData); continue; } string redfinedPrefab; if (colliderToPrefabs.TryGetValue(collider.name, out redfinedPrefab)) { EntityData entityData = new EntityData(); entityData.prefabName = redfinedPrefab; entityData.position = GetPosition(collider.transform); entityData.rotation = GetRotation(collider.transform); if (!buildingSiteData.decorEntities.Any(x => x.prefabName == entityData.prefabName && x.position == entityData.position && x.rotation == entityData.rotation)) buildingSiteData.decorEntities.Add(entityData); continue; } BaseEntity entity = collider.ToBaseEntity(); if (entity == null || entity is BasePlayer or BaseAnimalNPC or BaseCorpse or LootContainer or CCTV_RC or ReactiveTarget) continue; else { EntityData entityData = new EntityData(); entityData.prefabName = entity.PrefabName; entityData.skin = entity.skinID; entityData.position = GetPosition(entity.transform); entityData.rotation = GetRotation(entity.transform); if (entity is NPCDwelling or BaseOven or SimpleBuildingBlock or Barricade or CargoShipContainer || entity.ShortPrefabName == "door.hinged.shipping_container") { if (!buildingSiteData.decorEntities.Any(x => x.prefabName == entityData.prefabName && x.position == entityData.position && x.rotation == entityData.rotation)) buildingSiteData.decorEntities.Add(entityData); } else { if (!buildingSiteData.regularEntities.Any(x => x.prefabName == entityData.prefabName && x.position == entityData.position && x.rotation == entityData.rotation)) buildingSiteData.regularEntities.Add(entityData); } } float distanceToCenter = Vector3.Distance(collider.transform.position, loactionCenterPosition); } } static string GetBuildingBlockPrefabName(string colliderName) { if (colliderName.Contains("foundation")) { if (colliderName.Contains("triangle")) return "assets/prefabs/building core/foundation.triangle/foundation.triangle.prefab"; else if (colliderName.Contains("steps")) return "assets/prefabs/building core/foundation.steps/foundation.steps.prefab"; else return "assets/prefabs/building core/foundation/foundation.prefab"; } else if (colliderName.Contains("ramp")) { return "assets/prefabs/building core/ramp/ramp.prefab"; } else if (colliderName.Contains("floor")) { if (colliderName.Contains("frame")) { if (colliderName.Contains("triangle")) return "assets/prefabs/building core/floor.triangle.frame/floor.triangle.frame.prefab"; else return "assets/prefabs/building core/floor.frame/floor.frame.prefab"; } else if (colliderName.Contains("triangle")) return "assets/prefabs/building core/floor.triangle/floor.triangle.prefab"; else return "assets/prefabs/building core/floor/floor.prefab"; } else if (colliderName.Contains("wall")) { if (colliderName.Contains("doorway")) return "assets/prefabs/building core/wall.doorway/wall.doorway.prefab"; else if (colliderName.Contains("window")) return "assets/prefabs/building core/wall.window/wall.window.prefab"; else if (colliderName.Contains("frame")) return "assets/prefabs/building core/wall.frame/wall.frame.prefab"; else if (colliderName.Contains("half")) return "assets/prefabs/building core/wall.half/wall.half.prefab"; else if (colliderName.Contains("low")) return "assets/prefabs/building core/wall.low/wall.low.prefab"; else return "assets/prefabs/building core/wall/wall.prefab"; } else if (colliderName.Contains("stair")) { if (colliderName.Contains("spiral")) { if (colliderName.Contains("triangle")) return "assets/prefabs/building core/stairs.spiral.triangle/block.stair.spiral.triangle.prefab"; else return "assets/prefabs/building core/stairs.spiral/block.stair.spiral.prefab"; } else if (colliderName.Contains("ushape")) return "assets/prefabs/building core/stairs.u/block.stair.ushape.prefab"; else if (colliderName.Contains("lshape")) return "assets/prefabs/building core/stairs.l/block.stair.lshape.prefab"; } else if (colliderName.Contains("roof")) { if (colliderName.Contains("triangle")) return "assets/prefabs/building core/roof.triangle/roof.triangle.prefab"; else return "assets/prefabs/building core/roof/roof.prefab"; } return null; } static int GetBuildingBlockGrade(string colliderName) { if (colliderName.Contains("wood")) return 1; else if (colliderName.Contains("stone")) return 2; else if (colliderName.Contains("metal")) return 3; else if (colliderName.Contains("toptier")) return 4; return 0; } static string GetPosition(Transform transform) { Vector3 localPosition = transform.position - loactionCenterPosition; return $"({localPosition.x}, {localPosition.y}, {localPosition.z})"; } static string GetRotation(Transform transform) { return transform.eulerAngles.ToString(); } } static class NotifyManagerLite { internal static void PrintInfoMessage(BasePlayer player, string langKey, params object[] args) { if (player == null) ins.PrintWarning(ClearColorAndSize(GetMessage(langKey, null, args))); else ins.PrintToChat(player, GetMessage(langKey, player.UserIDString, args)); } internal static void PrintError(BasePlayer player, string langKey, params object[] args) { if (player == null) ins.PrintError(ClearColorAndSize(GetMessage(langKey, null, args))); else ins.PrintToChat(player, GetMessage(langKey, player.UserIDString, args)); } internal static void PrintLogMessage(string langKey, params object[] args) { for (int i = 0; i < args.Length; i++) if (args[i] is int) args[i] = GetTimeMessage(null, (int)args[i]); ins.Puts(ClearColorAndSize(GetMessage(langKey, null, args))); } internal static void PrintWarningMessage(string langKey, params object[] args) { ins.PrintWarning(ClearColorAndSize(GetMessage(langKey, null, args))); } internal static string ClearColorAndSize(string message) { message = message.Replace("", string.Empty); message = message.Replace("", string.Empty); while (message.Contains("", index) - index + 1); } while (message.Contains("", index) - index + 1); } return message; } internal static void SendMessageToAll(string langKey, params object[] args) { foreach (BasePlayer player in BasePlayer.activePlayerList) if (player != null) SendMessageToPlayer(player, langKey, args); } internal static void SendMessageToPlayer(BasePlayer player, string langKey, params object[] args) { object[] argsClone = new object[args.Length]; for (int i = 0; i < args.Length; i++) argsClone[i] = args[i]; for (int i = 0; i < argsClone.Length; i++) if (argsClone[i] is int) argsClone[i] = GetTimeMessage(player.UserIDString, (int)argsClone[i]); RedefinedMessageConfig redefinedMessageConfig = GetRedefinedMessageConfig(langKey); if (redefinedMessageConfig != null && !redefinedMessageConfig.isEnable) return; string playerMessage = GetMessage(langKey, player.UserIDString, args); if (redefinedMessageConfig != null) SendMessage(redefinedMessageConfig, player, playerMessage); else SendMessage(ins._config.notifyConfig, player, playerMessage); } static void SendMessage(BaseMessageConfig baseMessageConfig, BasePlayer player, string playerMessage) { if (baseMessageConfig.chatConfig.isEnabled) ins.PrintToChat(player, playerMessage); if (baseMessageConfig.gameTipConfig.isEnabled) player.SendConsoleCommand("gametip.showtoast", baseMessageConfig.gameTipConfig.style, ClearColorAndSize(playerMessage), string.Empty); } static RedefinedMessageConfig GetRedefinedMessageConfig(string langKey) { return ins._config.notifyConfig.redefinedMessages.FirstOrDefault(x => x.langKey == langKey); } internal static string GetTimeMessage(string userIDString, int seconds) { string message = ""; TimeSpan timeSpan = TimeSpan.FromSeconds(seconds); if (timeSpan.Hours > 0) message += $" {timeSpan.Hours} {GetMessage("Hours", userIDString)}"; if (timeSpan.Minutes > 0) message += $" {timeSpan.Minutes} {GetMessage("Minutes", userIDString)}"; if (message == "") message += $" {timeSpan.Seconds} {GetMessage("Seconds", userIDString)}"; return message; } } static class PositionDefiner { internal static Vector3 GetLocalPosition(Transform parentTransform, Vector3 globalPosition) { return parentTransform.InverseTransformPoint(globalPosition); } internal static Vector3 GetGlobalPosition(Transform parentTransform, Vector3 position) { return parentTransform.transform.TransformPoint(position); } internal static Quaternion GetGlobalRotation(Transform parentTransform, Vector3 rotation) { return parentTransform.rotation * Quaternion.Euler(rotation); } internal static Vector3 GetRandomMapPoint() { Vector2 randomVector2 = World.Size * 0.6f * UnityEngine.Random.insideUnitCircle; Vector3 randomVector3 = PositionDefiner.GetGroundPosition(new Vector3(randomVector2.x, 0, randomVector2.y)); return randomVector3; } internal static Vector3 GetGroundPosition(Vector3 position) { position.y = 400; RaycastHit raycastHit; int layerMask = 1 << 16 | 1 << 23; if (Physics.Raycast(position, Vector3.down, out raycastHit, 500, layerMask)) position.y = raycastHit.point.y; else position.y = 0; return position; } internal static BaseEntity RaycastAll(Ray ray, float distance = 50) where T : BaseEntity { RaycastHit[] hits = Physics.RaycastAll(ray); GamePhysics.Sort(hits); BaseEntity target = null; foreach (RaycastHit hit in hits) { BaseEntity ent = hit.GetEntity(); if (ent is T && hit.distance < distance) { target = ent; break; } } return target; } internal static bool IsPositionOnCargoPath(Vector3 position) { foreach (CargoShip.HarborInfo harbotInfo in CargoShip.harbors) { IAIPathNode pathNode = harbotInfo.harborPath.GetClosestToPoint(position); if (Vector3.Distance(pathNode.Position, position) < 90) return true; } float distanceToCargoPath = GetDistanceToCargoPath(position); return distanceToCargoPath < 100; } static float GetDistanceToCargoPath(Vector3 position) { int index = GetNearIndexPathCargo(position); int indexNext = TerrainMeta.Path.OceanPatrolFar.Count - 1 == index ? 0 : index + 1; int indexPrevious = index == 0 ? TerrainMeta.Path.OceanPatrolFar.Count - 1 : index - 1; float distanceNext = GetDistanceToCargoPath(position, index, indexNext); float distancePrevious = GetDistanceToCargoPath(position, indexPrevious, index); return distanceNext < distancePrevious ? distanceNext : distancePrevious; } static int GetNearIndexPathCargo(Vector3 position) { int index = 0; float distance = float.MaxValue; for (int i = 0; i < TerrainMeta.Path.OceanPatrolFar.Count; i++) { Vector3 vector3 = TerrainMeta.Path.OceanPatrolFar[i]; float single = Vector3.Distance(position, vector3); if (single < distance) { index = i; distance = single; } } return index; } static float GetDistanceToCargoPath(Vector3 position, int index1, int index2) { Vector3 pos1 = TerrainMeta.Path.OceanPatrolFar[index1]; Vector3 pos2 = TerrainMeta.Path.OceanPatrolFar[index2]; float distance1 = Vector3.Distance(position, pos1); float distance2 = Vector3.Distance(position, pos2); float distance12 = Vector3.Distance(pos1, pos2); float p = (distance1 + distance2 + distance12) / 2; return (2 / distance12) * (float)Math.Sqrt(p * (p - distance1) * (p - distance2) * (p - distance12)); } } static class LootManager { internal static void GiveItemToPLayer(BasePlayer player, ItemConfig itemConfig, int amount) { Item item = CreateItem(itemConfig, amount); if (item == null) return; GiveItemToPLayer(player, item); } static void GiveItemToPLayer(BasePlayer player, Item item) { int slots = player.inventory.containerMain.capacity + player.inventory.containerBelt.capacity; int taken = player.inventory.containerMain.itemList.Count + player.inventory.containerBelt.itemList.Count; if (slots - taken > 0) player.inventory.GiveItem(item); else item.Drop(player.transform.position, Vector3.up); } internal static Item CreateItem(ItemConfig itemConfig, int amount) { Item item = ItemManager.CreateByName(itemConfig.shortname, amount, itemConfig.skin); if (itemConfig.name != "") item.name = itemConfig.name; return item; } } static class PermissionManager { internal static void RegisterPermissions() { foreach (BaseSiteConfig baseSiteConfig in ins._config.groundTypeConfig.sites) if (!string.IsNullOrEmpty(baseSiteConfig.permission)) ins.permission.RegisterPermission(baseSiteConfig.permission, ins); foreach (BaseSiteConfig baseSiteConfig in ins._config.waterTypeConfig.sites) if (!string.IsNullOrEmpty(baseSiteConfig.permission)) ins.permission.RegisterPermission(baseSiteConfig.permission, ins); foreach (BaseSiteConfig baseSiteConfig in ins._config.skyTypeConfig.sites) if (!string.IsNullOrEmpty(baseSiteConfig.permission)) ins.permission.RegisterPermission(baseSiteConfig.permission, ins); } internal static bool IsUserHavePermission(string userIdString, string permissionName) { return ins.permission.UserHasPermission(userIdString, permissionName); } } #endregion Classes #region Data Dictionary siteCustomizationDatas = new Dictionary(); bool TryLoadData() { foreach (BaseSiteConfig siteConfig in _config.groundTypeConfig.sites) { if (!TryLoadSiteDataFile(siteConfig.dataFileName)) return false; } foreach (BaseSiteConfig siteConfig in _config.waterTypeConfig.sites) { if (!TryLoadSiteDataFile(siteConfig.dataFileName)) return false; } foreach (BaseSiteConfig siteConfig in _config.skyTypeConfig.sites) { if (!TryLoadSiteDataFile(siteConfig.dataFileName)) return false; } return true; } bool TryLoadSiteDataFile(string path) { BuildingSiteData siteData = LoadDataFile($"{path}"); if (siteData == null || siteData.buildingBlocks == null) return false; if (!siteCustomizationDatas.ContainsKey(path)) siteCustomizationDatas.Add(path, siteData); return true; } Type LoadDataFile(string path) { string fullPath = $"{ins.Name}/{path}"; return Interface.Oxide.DataFileSystem.ReadObject(fullPath); } void SaveDataFile(Type objectForSaving, string path) { string fullPath = $"{ins.Name}/{path}"; Interface.Oxide.DataFileSystem.WriteObject(fullPath, objectForSaving); } public class BuildingSiteData { [JsonProperty("Offset")] public string offset { get; set; } [JsonProperty("Building blocks")] public HashSet buildingBlocks { get; set; } [JsonProperty("Regular Entities")] public HashSet regularEntities { get; set; } [JsonProperty("Decor Entities")] public HashSet decorEntities { get; set; } [JsonProperty("Prevent Buildong Colliders")] public HashSet preventBuildingColliders { get; set; } [JsonProperty("Prevent Movement Colliders")] public HashSet preventMovementColliders { get; set; } [JsonProperty("Wire Datas")] public HashSet wireDatas { get; set; } } public class BuildingBlockData : EntityData { [JsonProperty("Grade [0 - 4]", Order = 102)] public int grade { get; set; } [JsonProperty("Color", Order = 103)] public uint color { get; set; } } public class EntityData : LocationData { [JsonProperty("Prefab")] public string prefabName { get; set; } [JsonProperty("Skin")] public ulong skin { get; set; } } public class BoxColliderData : LocationData { [JsonProperty("Size")] public string size { get; set; } } public class LocationData { [JsonProperty("Position", Order = 100)] public string position { get; set; } [JsonProperty("Rotation", Order = 101)] public string rotation { get; set; } } public class WireData { [JsonProperty("Start Position")] public string startPosition { get; set; } [JsonProperty("End Position")] public string endPosition { get; set; } } #endregion Data #region Lang protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["ConfigNotFound_Exeption"] = "Конфигурация не найдена! ({0})", ["GroundFlare_Description"] = "{0} Найдите плоскую поверхность и бросьте флаер на землю. Проще всего найти место на пляже", ["WaterFlare_Description"] = "{0} Бросьте флаер в море в отдалении от маршрута карго", ["SkyFlare_Description"] = "{0} Бросьте флаер на землю и локация появится над вами", ["Spawn_Failed"] = "Неудалось заспавнить!", ["NoPermission"] = "У вас нет разрешения!", ["Unauthorized"] = "Вы не авторизованы в шкафу!", ["BlockedOnSite"] = "Этот предмет запрещен!", ["MaxSitesForPlayer"] = "Вы уже призвали максимальное число локаций!", ["Spawn_Start"] = "Начался спавн локации!", ["GotSite"] = "Вы получили место для строительства!", ["UnsuitableLandscape_BlockSpawn"] = "Неподходящий рельеф!", ["WrongPlace_BlockSpawn"] = "Неподходящее место!", ["WrongBiome_BlockSpawn"] = "Неподходящий биом!", ["CloseSite_BlockSpawn"] = "Слишком близко к другому месту строительства!", ["Object_BlockSpawn"] = "Объект блокирует размещение!", ["Player_BlockSpawn"] = "Игрок блокирует размещение!", ["FarShore_BlockSpawn"] = "Слишком далеко от берега!", ["InsufficientDepth_BlockSpawn"] = "Недостаточная глубина!", ["Cargo_BlockSpawn"] = "Слишком близко к маршруту карго!", ["CantFindPosition_Exeption"] = "Не удалось найти подходящую позицию для спавна локации! ({0})", ["Position_Suitable"] = "Киньте флаер в свою позицию и отступите на {0} метров!", }, this, "ru"); lang.RegisterMessages(new Dictionary { ["ConfigNotFound_Exeption"] = "Configuration not found! ({0})", ["DataFileNotFound_Exeption"] = "Data file not found! ({0})", ["PlayerNotFound_Exeption"] = "Player not found! ({0})", ["DataNotFound_Exeption"] = "Data files were not found, or are corrupted. Move the contents of the data folder from the archive to the oxide/data folder on your server!", ["SiteSpawn_Log"] = "{0} is spawned at grid {1}", ["GroundFlare_Description"] = "{0} Find a flat surface and drop the flare to the ground. The easiest way is to find a place on the beach", ["WaterFlare_Description"] = "{0} Throw the flare into the sea away from the cargo route", ["SkyFlare_Description"] = "{0} Throw the flare to the ground and the building site will spawn above you", ["Spawn_Failed"] = "Failed to spawn!", ["NoPermission"] = "You don't have permission!", ["Unauthorized"] = "You are not authorized!", ["BlockedOnSite"] = "This item is prohibited!", ["MaxSitesForPlayer"] = "You have already summoned up the maximum number of locations!", ["UnsuitableLandscape_BlockSpawn"] = "Unsuitable landscape!", ["WrongPlace_BlockSpawn"] = "The wrong place!", ["WrongBiome_BlockSpawn"] = "Wrong biome!", ["CloseSite_BlockSpawn"] = "Too close to another building site!", ["Object_BlockSpawn"] = "An object is blocking the placement!", ["Player_BlockSpawn"] = "A player is blocking the placement!", ["FarShore_BlockSpawn"] = "Too far from the shore!", ["InsufficientDepth_BlockSpawn"] = "Insufficient depth!", ["Cargo_BlockSpawn"] = "Too close to the cargo ship's route!", ["CantFindPosition_Exeption"] = "Couldn't find a suitable position to spawn the site! ({0})", ["Position_Suitable"] = "Throw the flyer to your position and run {0} meters away!", ["Spawn_Start"] = "The spawn has begun!", ["GotSite"] = "You've got a Building Site!", }, this); } internal static string GetMessage(string langKey, string userID) { return ins.lang.GetMessage(langKey, ins, userID); } internal static string GetMessage(string langKey, string userID, params object[] args) { return (args.Length == 0) ? GetMessage(langKey, userID) : string.Format(GetMessage(langKey, userID), args); } #endregion Lang #region Configs private PluginConfig _config; protected override void LoadDefaultConfig() { _config = PluginConfig.DefaultConfig(); } protected override void LoadConfig() { base.LoadConfig(); _config = Config.ReadObject(); Config.WriteObject(_config, true); } protected override void SaveConfig() { Config.WriteObject(_config); } public class MainConfig { [JsonProperty(en ? "The maximum number of locations that one player can summon (-1 - not limited)" : "Максимальное количество локаций, которое может вызвать один игрок (-1 - не ограничивать)", Order = 100)] public int maxLocationNumberPerPlayer { get; set; } [JsonProperty(en ? "Kill trees in the spawn position of locations? [true/false]" : "Удалять деревья в позиции спавна локаций? [true/false]", Order = 101)] public bool killTrees { get; set; } [JsonProperty(en ? "Will only authorized players be able to loot pumpjacks on the Building Site? [true/false]" : "Насосы на BuildingSite смогут лутать только авторизированные игроки? [true/false]", Order = 102)] public bool onlyAutedLootPumpjack { get; set; } [JsonProperty(en ? "Enable site spawn logging [true/false]" : "Включить логирование спавна локаций [true/false]")] public bool isSpawnLogging { get; set; } } public class GroundTypeConfig : BaseTypeConfig { } public class WaterTypeConfig : BaseTypeConfig { [JsonProperty(en ? "Minimum depth" : "Минимальная глубина", Order = 102)] public float minDepth { get; set; } } public class SkyTypeConfig : BaseTypeConfig { [JsonProperty(en ? "Minimum spawn altitude" : "Минмальная высота спавна", Order = 100)] public int minSpawnHeight { get; set; } [JsonProperty(en ? "Maximum spawn altitude" : "Максимальная высота спавна", Order = 101)] public int maxSpawnHeight { get; set; } [JsonProperty(en ? "Prohibit the deployment of turrets for locations of this type." : "Запретить устанавливать турели на локациях этого типа", Order = 103)] public bool isTurretsDisable { get; set; } [JsonProperty(en ? "Prohibit the deployment of SamSites for locations of this type." : "Запретить устанавливать ПВО на локациях этого типа", Order = 104)] public bool isSamsiteDisable { get; set; } } public class BaseTypeConfig { [JsonProperty(en ? "Allow automatic spawn? [true/false]" : "Разрешить автоматический спавн? [true/false]")] public bool isAutoSpawn { get; set; } [JsonProperty(en ? "The minimum number of locations" : "Минимальное количество локаций этого типа", Order = 1)] public int minAmount { get; set; } [JsonProperty(en ? "The maximum number of locations" : "Максимальное количество локаций этого типа", Order = 2)] public int maxAmount { get; set; } [JsonProperty(en ? "List of locations" : "Список локаций", Order = 1000)] public HashSet sites { get; set; } } public class BaseSiteConfig { [JsonProperty(en ? "Preset Name" : "Название пресета")] public string presetName { get; set; } [JsonProperty(en ? "Data file Name" : "Название дата файла")] public string dataFileName { get; set; } [JsonProperty(en ? "Radius of the construction area" : "Радиус зоны для постройки")] public float internalRadius { get; set; } [JsonProperty(en ? "Location radius" : "Радиус локации")] public float externalRadius { get; set; } [JsonProperty(en ? "Allow automatic spawn? [true/false]" : "Разрешить автоматический спавн? [true/false]")] public bool isAutoSpawn { get; set; } [JsonProperty(en ? "Probability " : "Вероятность спавна")] public float probability { get; set; } [JsonProperty(en ? "Biomes for automatic location spawn (Arid, Temperate, Tundra, Arctic)" : "Биомы для автоматического спавна локации (Arid, Temperate, Tundra, Arctic)")] public HashSet biomes { get; set; } [JsonProperty(en ? "Permission to summon BuildingSite" : "Разрешение для призыва локации", NullValueHandling = NullValueHandling.Ignore)] public string permission { get; set; } [JsonProperty(en ? "Maximum upward deviation in height" : "Максимальное отклонение по высоте вверх", NullValueHandling = NullValueHandling.Ignore)] public float maxUpDeltaHeigh { get; set; } [JsonProperty(en ? "Maximum downward deviation in height" : "Максимальное отклонение по высоте вниз", NullValueHandling = NullValueHandling.Ignore)] public float maxDownDeltaHeigh { get; set; } [JsonProperty(en ? "Item" : "Предмет для спавна")] public SummonMonumentConfig summonConfig { get; set; } } public class SummonMonumentConfig : ItemConfig { [JsonProperty(en ? "Lang instructions for spawn locations" : "Lang инструкция спавна локации", Order = 100)] public string spawnDescriptionLang { get; set; } [JsonProperty(en ? "Permission to summon BuildingSite" : "Разрешение для призыва локации", Order = 100)] public string permission { get; set; } } public class ItemConfig { [JsonProperty("ShortName")] public string shortname { get; set; } [JsonProperty("SkinID (0 - default)")] public ulong skin { get; set; } [JsonProperty(en ? "Name (empty - default)" : "Название (empty - default)")] public string name { get; set; } } public class MarkerConfig { [JsonProperty(en ? "Гse a marker for free locations? [true/false]" : "Использовать маркер для свободных локаций? [true/false]")] public bool enable { get; set; } [JsonProperty(en ? "Display Name" : "Отображаемое имя")] public string displayedName { get; set; } [JsonProperty(en ? "Use a vending marker? [true/false]" : "Добавить маркер магазина? [true/false]")] public bool useShopMarker { get; set; } [JsonProperty(en ? "Use a circular marker? [true/false]" : "Добавить круговой маркер? [true/false]")] public bool useRingMarker { get; set; } [JsonProperty(en ? "Radius" : "Радиус")] public float radius { get; set; } [JsonProperty(en ? "Alpha" : "Прозрачность")] public float alpha { get; set; } [JsonProperty(en ? "Marker color" : "Цвет маркера")] public ColorConfig color1 { get; set; } [JsonProperty(en ? "Outline color" : "Цвет контура")] public ColorConfig color2 { get; set; } } public class ColorConfig { [JsonProperty("r")] public float r { get; set; } [JsonProperty("g")] public float g { get; set; } [JsonProperty("b")] public float b { get; set; } } public class NotifyConfig : BaseMessageConfig { [JsonProperty(en ? "Redefined messages" : "Переопределенные сообщения )", Order = 101)] public HashSet redefinedMessages { get; set; } } public class RedefinedMessageConfig : BaseMessageConfig { [JsonProperty(en ? "Enable this message? [true/false]" : "Включить сообщение? [true/false]", Order = 1)] public bool isEnable { get; set; } [JsonProperty("Lang Key", Order = 1)] public string langKey { get; set; } } public class BaseMessageConfig { [JsonProperty(en ? "Chat Message setting" : "Настройки сообщений в чате", Order = 1)] public ChatConfig chatConfig { get; set; } [JsonProperty(en ? "Facepunch Game Tips setting" : "Настройка сообщений Facepunch Game Tip", Order = 2)] public GameTipConfig gameTipConfig { get; set; } } public class ChatConfig { [JsonProperty(en ? "Use chat notifications? [true/false]" : "Использовать ли чат? [true/false]")] public bool isEnabled { get; set; } } public class GameTipConfig { [JsonProperty(en ? "Use Facepunch Game Tips (notification bar above hotbar)? [true/false]" : "Использовать ли Facepunch Game Tip (оповещения над слотами быстрого доступа игрока)? [true/false]")] public bool isEnabled { get; set; } [JsonProperty(en ? "Style (0 - Blue Normal, 1 - Red Normal, 2 - Blue Long, 3 - Blue Short, 4 - Server Event)" : "Стиль (0 - Blue Normal, 1 - Red Normal, 2 - Blue Long, 3 - Blue Short, 4 - Server Event)")] public int style { get; set; } } private class PluginConfig { [JsonProperty(en ? "Version" : "Версия")] public VersionNumber version { get; set; } [JsonProperty(en ? "Chat Prefix" : "Префикс в чате")] public string prefix { get; set; } [JsonProperty(en ? "General Setting" : "Основные настройки")] public MainConfig mainConfig { get; set; } [JsonProperty(en ? "Ground locations" : "Наземные локации")] public GroundTypeConfig groundTypeConfig { get; set; } [JsonProperty(en ? "Islands" : "Острова")] public WaterTypeConfig waterTypeConfig { get; set; } [JsonProperty(en ? "Sky locations" : "Воздушные локации")] public SkyTypeConfig skyTypeConfig { get; set; } [JsonProperty(en ? "Marker Setting" : "Настройки маркера")] public MarkerConfig markerConfig { get; set; } [JsonProperty(en ? "Notification Settings" : "Настройки уведомлений")] public NotifyConfig notifyConfig { get; set; } public static PluginConfig DefaultConfig() { return new PluginConfig() { version = new VersionNumber(1, 1, 1), prefix = "[BuildingSites]", mainConfig = new MainConfig { maxLocationNumberPerPlayer = -1, isSpawnLogging = true }, groundTypeConfig = new GroundTypeConfig { isAutoSpawn = false, minAmount = 3, maxAmount = 3, sites = new HashSet { new BaseSiteConfig { presetName = "cave_1", dataFileName = "cave_1", internalRadius = 21, externalRadius = 40, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387696053, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 4f, maxDownDeltaHeigh = 4f }, new BaseSiteConfig { presetName = "platform_1", dataFileName = "platform_1", internalRadius = 5, externalRadius = 41, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387684797, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 5f, maxDownDeltaHeigh = 9f }, new BaseSiteConfig { presetName = "platform_2", dataFileName = "platform_2", internalRadius = 40, externalRadius = 45, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387684995, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 16, maxDownDeltaHeigh = 8 }, new BaseSiteConfig { presetName = "ruins_1", dataFileName = "ruins_1", internalRadius = 41, externalRadius = 55, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387685498, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 5, maxDownDeltaHeigh = 10 }, new BaseSiteConfig { presetName = "ruins_2", dataFileName = "ruins_2", internalRadius = 14, externalRadius = 30, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387686922, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 5, maxDownDeltaHeigh = 10 }, new BaseSiteConfig { presetName = "ruins_3", dataFileName = "ruins_3", internalRadius = 26, externalRadius = 35, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387686321, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 3.5f, maxDownDeltaHeigh = 10f }, new BaseSiteConfig { presetName = "ruins_4", dataFileName = "ruins_4", internalRadius = 18, externalRadius = 35, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387685895, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 8f, maxDownDeltaHeigh = 8f }, new BaseSiteConfig { presetName = "ruins_5", dataFileName = "ruins_5", internalRadius = 9f, externalRadius = 18f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387687136, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 5f, maxDownDeltaHeigh = 10f }, new BaseSiteConfig { presetName = "bunker_1", dataFileName = "bunker_1", internalRadius = 12, externalRadius = 35, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387687426, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 4f, maxDownDeltaHeigh = 4f }, new BaseSiteConfig { presetName = "bunker_2", dataFileName = "bunker_2", internalRadius = 17f, externalRadius = 36.7f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Building Site", skin = 3387687572, spawnDescriptionLang = "GroundFlare_Description", permission = "" }, maxUpDeltaHeigh = 5f, maxDownDeltaHeigh = 5f } } }, waterTypeConfig = new WaterTypeConfig { isAutoSpawn = false, minAmount = 2, maxAmount = 2, minDepth = 4f, sites = new HashSet { new BaseSiteConfig { presetName = "island_1", dataFileName = "island_1", internalRadius = 42.2f, externalRadius = 50f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Water Building Site", skin = 3387687815, spawnDescriptionLang = "WaterFlare_Description", permission = "" } }, new BaseSiteConfig { presetName = "island_2", dataFileName = "island_2", internalRadius = 40f, externalRadius = 50f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Water Building Site", skin = 3387688043, spawnDescriptionLang = "WaterFlare_Description", permission = "" } }, new BaseSiteConfig { presetName = "island_3", dataFileName = "island_3", internalRadius = 63, externalRadius = 75f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Water Building Site", skin = 3387688286, spawnDescriptionLang = "WaterFlare_Description", permission = "" } }, new BaseSiteConfig { presetName = "island_4", dataFileName = "island_4", internalRadius = 43, externalRadius = 50f, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Water Building Site", skin = 3387688429, spawnDescriptionLang = "WaterFlare_Description", permission = "" } }, } }, skyTypeConfig = new SkyTypeConfig { isAutoSpawn = false, minAmount = 1, maxAmount = 1, minSpawnHeight = 100, maxSpawnHeight = 160, isSamsiteDisable = false, isTurretsDisable = false, sites = new HashSet { new BaseSiteConfig { presetName = "sky_1", dataFileName = "sky_1", internalRadius = 43f, externalRadius = 50, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Flying Building Site", skin = 3387688822, spawnDescriptionLang = "SkyFlare_Description", permission = "" } }, new BaseSiteConfig { presetName = "sky_2", dataFileName = "sky_2", internalRadius = 32, externalRadius = 40, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Flying Building Site", skin = 3387689017, spawnDescriptionLang = "SkyFlare_Description", permission = "" } }, new BaseSiteConfig { presetName = "sky_3", dataFileName = "sky_3", internalRadius = 26, externalRadius = 35, isAutoSpawn = true, probability = 20f, biomes = new HashSet(), summonConfig = new SummonMonumentConfig { shortname = "flare", name = "Flying Building Site", skin = 3387689198, spawnDescriptionLang = "SkyFlare_Description", permission = "" } } } }, markerConfig = new MarkerConfig { enable = true, displayedName = en ? "Free building site" : "Свободное место под застройку", useRingMarker = true, useShopMarker = true, radius = 0.2f, alpha = 0.6f, color1 = new ColorConfig { r = 0.2f, g = 0.8f, b = 0.1f }, color2 = new ColorConfig { r = 0f, g = 0f, b = 0f } }, notifyConfig = new NotifyConfig { chatConfig = new ChatConfig { isEnabled = false, }, gameTipConfig = new GameTipConfig { isEnabled = true, style = 1, }, redefinedMessages = new HashSet { new RedefinedMessageConfig { isEnable = true, langKey = "Position_Suitable", chatConfig = new ChatConfig { isEnabled = false, }, gameTipConfig = new GameTipConfig { isEnabled = true, style = 2, } }, new RedefinedMessageConfig { isEnable = true, langKey = "GotSite", chatConfig = new ChatConfig { isEnabled = false, }, gameTipConfig = new GameTipConfig { isEnabled = true, style = 2, } }, new RedefinedMessageConfig { isEnable = true, langKey = "GroundFlare_Description", chatConfig = new ChatConfig { isEnabled = true, }, gameTipConfig = new GameTipConfig { isEnabled = false, style = 2, } }, new RedefinedMessageConfig { isEnable = true, langKey = "WaterFlare_Description", chatConfig = new ChatConfig { isEnabled = true, }, gameTipConfig = new GameTipConfig { isEnabled = false, style = 2, } }, new RedefinedMessageConfig { isEnable = true, langKey = "SkyFlare_Description", chatConfig = new ChatConfig { isEnabled = true, }, gameTipConfig = new GameTipConfig { isEnabled = false, style = 2, } } } }, }; } } #endregion Config } } namespace Oxide.Plugins.BuildingSitesExtensionMethods { public static class ExtensionMethods { public static bool Any(this IEnumerable source, Func predicate) { using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) return true; return false; } public static HashSet Where(this IEnumerable source, Func predicate) { HashSet result = new HashSet(); using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) result.Add(enumerator.Current); return result; } public static TSource FirstOrDefault(this IEnumerable source, Func predicate) { using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (predicate(enumerator.Current)) return enumerator.Current; return default(TSource); } public static HashSet Select(this IEnumerable source, Func predicate) { HashSet result = new HashSet(); using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) result.Add(predicate(enumerator.Current)); return result; } public static List Select(this IList source, Func predicate) { List result = new List(); for (int i = 0; i < source.Count; i++) { TSource element = source[i]; result.Add(predicate(element)); } return result; } public static bool IsExists(this BaseNetworkable entity) => entity != null && !entity.IsDestroyed; public static bool IsRealPlayer(this BasePlayer player) => player != null && player.userID.IsSteamId(); public static List OrderBy(this IEnumerable source, Func predicate) { List result = source.ToList(); for (int i = 0; i < result.Count; i++) { for (int j = 0; j < result.Count - 1; j++) { if (predicate(result[j]) > predicate(result[j + 1])) { TSource z = result[j]; result[j] = result[j + 1]; result[j + 1] = z; } } } return result; } public static List ToList(this IEnumerable source) { List result = new List(); using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) result.Add(enumerator.Current); return result; } public static HashSet ToHashSet(this IEnumerable source) { HashSet result = new HashSet(); using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) result.Add(enumerator.Current); return result; } public static HashSet OfType(this IEnumerable source) { HashSet result = new HashSet(); using (var enumerator = source.GetEnumerator()) while (enumerator.MoveNext()) if (enumerator.Current is T) result.Add((T)(object)enumerator.Current); return result; } public static TSource Max(this IEnumerable source, Func predicate) { TSource result = source.ElementAt(0); float resultValue = predicate(result); using (var enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { TSource element = enumerator.Current; float elementValue = predicate(element); if (elementValue > resultValue) { result = element; resultValue = elementValue; } } } return result; } public static TSource Min(this IEnumerable source, Func predicate) { TSource result = source.ElementAt(0); float resultValue = predicate(result); using (var enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { TSource element = enumerator.Current; float elementValue = predicate(element); if (elementValue < resultValue) { result = element; resultValue = elementValue; } } } return result; } public static TSource ElementAt(this IEnumerable source, int index) { int movements = 0; using (var enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { if (movements == index) return enumerator.Current; movements++; } } return default(TSource); } public static TSource First(this IList source) => source[0]; public static TSource Last(this IList source) => source[source.Count - 1]; public static bool IsEqualVector3(this Vector3 a, Vector3 b) => Vector3.Distance(a, b) < 0.1f; public static List OrderByQuickSort(this List source, Func predicate) { return source.QuickSort(predicate, 0, source.Count - 1); } private static List QuickSort(this List source, Func predicate, int minIndex, int maxIndex) { if (minIndex >= maxIndex) return source; int pivotIndex = minIndex - 1; for (int i = minIndex; i < maxIndex; i++) { if (predicate(source[i]) < predicate(source[maxIndex])) { pivotIndex++; source.Replace(pivotIndex, i); } } pivotIndex++; source.Replace(pivotIndex, maxIndex); QuickSort(source, predicate, minIndex, pivotIndex - 1); QuickSort(source, predicate, pivotIndex + 1, maxIndex); return source; } private static void Replace(this IList source, int x, int y) { TSource t = source[x]; source[x] = source[y]; source[y] = t; } public static object GetPrivateFieldValue(this object obj, string fieldName) { FieldInfo fi = GetPrivateFieldInfo(obj.GetType(), fieldName); if (fi != null) return fi.GetValue(obj); else return null; } public static void SetPrivateFieldValue(this object obj, string fieldName, object value) { FieldInfo info = GetPrivateFieldInfo(obj.GetType(), fieldName); if (info != null) info.SetValue(obj, value); } public static FieldInfo GetPrivateFieldInfo(Type type, string fieldName) { foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) if (fi.Name == fieldName) return fi; return null; } public static Action GetPrivateAction(this object obj, string methodName) { MethodInfo mi = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); if (mi != null) return (Action)Delegate.CreateDelegate(typeof(Action), obj, mi); else return null; } public static object CallPrivateMethod(this object obj, string methodName, params object[] args) { MethodInfo mi = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); if (mi != null) return mi.Invoke(obj, args); else return null; } } }