C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
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", "Forum: https://topplugin.ru Ds: alone_sempai Vk: https://vk.com/rustnastroika", "1.0.7")]
class BuildingSites : RustPlugin {
#region Variables
static BuildingSites ins;
const bool en = false;
HashSet<string> subscribeMethods = new HashSet<string> {
"CanBuild", "OnEntitySpawned", "OnExplosiveThrown", "OnExplosiveDropped", "OnActiveItemChanged", "CanLootEntity", "OnEntityTakeDamage", "CanEntityTakeDamage"
}
;
#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;
}
BuildingSite.Initialize();
PlayerSiteSpawner.StartUpdate();
PermissionManager.RegisterPermissions();
Subscribes();
}
void Unload() {
BuildingSite.UnloadSites();
SiteSpawner.StopAutoSpawn();
PlayerSiteSpawner.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<SkySiteConfig>(constructionTarget.position, out distance);
if (buildingSite == null) return null;
if (distance < buildingSite.siteConfig.outsideRadius * 1.5f) {
NotifyManagerLite.SendMessageToPlayer(player, "BlockedOnSite");
return true;
}
}
return null;
}
void OnEntitySpawned(BuildingPrivlidge buildingPrivlidge) {
if (buildingPrivlidge == null) return;
NextTick(() => {
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;
BaseSiteConfig baseSiteConfig = BuildingSite.GetSiteConfigBySkinId(item.skin);
if (baseSiteConfig == null) return;
SiteSpawnFlare.Attach(roadFlare, player, baseSiteConfig);
}
void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) {
if (!player.IsRealPlayer() || newItem == null) return;
if (newItem != null && newItem.info.shortname == "flare") {
BaseSiteConfig baseSiteConfig = BuildingSite.GetSiteConfigBySkinId(newItem.skin);
if (baseSiteConfig == null) return;
BuildingSiteData siteData;
ins.siteCustomizationDatas.TryGetValue(baseSiteConfig.dataFileName, out siteData);
if (siteData == null) return;
PlayerSiteSpawner.AddPlayer(player, baseSiteConfig);
GroundSiteConfig groundSiteConfig = baseSiteConfig as GroundSiteConfig;
if (groundSiteConfig != null) {
NotifyManagerLite.SendMessageToPlayer(player, "GroundFlare_Description", _config.prefix);
return;
}
WaterSiteConfig waterSiteConfig = baseSiteConfig as WaterSiteConfig;
if (waterSiteConfig != null) {
NotifyManagerLite.SendMessageToPlayer(player, "WaterFlare_Description", _config.prefix);
return;
}
SkySiteConfig skySiteConfig = baseSiteConfig as SkySiteConfig;
if (skySiteConfig != null) {
NotifyManagerLite.SendMessageToPlayer(player, "SkyFlare_Description", _config.prefix);
return;
}
}
}
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.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;
}
_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;
SiteSpawner.StartAutoSpawn();
NotifyManagerLite.SendMessageToPlayer(player, "Spawn_Start");
}
[ConsoleCommand("spawnsite")]
void RespawnSitesConsoleCommand(ConsoleSystem.Arg arg) {
if (arg.Player() != null) return;
SiteSpawner.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;
}
SiteSpawner.SpawnSiteInRandomLocation(presetName);
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;
}
SiteSpawner.SpawnSiteInRandomLocation(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.itemConfig, 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.itemConfig, 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<BaseEntity>(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 PlayerSiteSpawner {
static HashSet<PlayerFlareInfo> playersWithFlare = new HashSet<PlayerFlareInfo>();
static Coroutine updateCorountine;
internal static void AddPlayer(BasePlayer player, BaseSiteConfig baseSiteConfig) {
if (playersWithFlare.Any(x => x.player != null && x.player.userID == player.userID)) return;
PlayerFlareInfo playerFlareData = new PlayerFlareInfo(player, baseSiteConfig);
playersWithFlare.Add(playerFlareData);
}
internal static void StartUpdate() {
updateCorountine = ServerMgr.Instance.StartCoroutine(SpawnLocationCoroutine());
}
internal static void StopUpdate() {
if (updateCorountine != null) ServerMgr.Instance.StopCoroutine(updateCorountine);
}
static IEnumerator SpawnLocationCoroutine() {
while (true) {
playersWithFlare.RemoveWhere(x => !IsFlareDataActive(x));
foreach (PlayerFlareInfo playerFlareData in playersWithFlare) {
DisplayPlayerData(playerFlareData);
}
yield return CoroutineEx.waitForSeconds(1f);
}
}
static void DisplayPlayerData(PlayerFlareInfo playerFlareData) {
if (!string.IsNullOrEmpty(playerFlareData.siteConfig.permission) && !PermissionManager.IsUserHavePermission(playerFlareData.player.UserIDString, playerFlareData.siteConfig.permission)) {
NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "NoPermission");
return;
}
HashSet<BaseEntity> entities = new HashSet<BaseEntity>();
Vector3 spawnPosition = playerFlareData.player.transform.position;
bool isPlacesuitable = SiteSpawner.IsPositionAvilable(ref spawnPosition, playerFlareData.siteConfig, out entities, true);
if (isPlacesuitable) NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "Position_Suitable");
else NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "Position_NotSuitable");
}
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, BaseSiteConfig siteConfig) {
SiteSpawnFlare siteSpawnFlare = roadFlare.gameObject.AddComponent<SiteSpawnFlare>();
siteSpawnFlare.Init(roadFlare, player, siteConfig);
return siteSpawnFlare;
}
void Init(RoadFlare roadFlare, BasePlayer player, BaseSiteConfig siteConfig) {
playerFlareData = new PlayerFlareInfo(player, siteConfig);
this.roadFlare = roadFlare;
roadFlare.enableSaving = false;
roadFlare.waterCausesExplosion = false;
if (!string.IsNullOrEmpty(siteConfig.permission) && !PermissionManager.IsUserHavePermission(player.UserIDString, siteConfig.permission)) {
LootManager.GiveItemToPLayer(player, playerFlareData.siteConfig.itemConfig, 1);
NotifyManagerLite.SendMessageToPlayer(player, "NoPermission");
roadFlare.Kill();
return;
}
roadFlare.Invoke(CallSite, siteConfig is WaterSiteConfig ? 30f : 15f);
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(100, float.MaxValue);
if (siteConfig is WaterSiteConfig) roadFlare.InvokeRepeating(WaterCheck, 1f, 1f);
}
void OnCollisionEnter(Collision collision) {
Rigidbody rigidbody = roadFlare.GetComponent<Rigidbody>();
rigidbody.isKinematic = true;
}
void WaterCheck() {
if (roadFlare.transform.position.y <= 0) {
Rigidbody rigidbody = roadFlare.GetComponent<Rigidbody>();
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<BuildingSite> playerSites = BuildingSite.GetPlayersSites(playerFlareData.player.userID);
int playerSitesCount = playerSites.Count;
if (playerSitesCount >= ins._config.mainConfig.maxLocationNumberPerPlayer) {
LootManager.GiveItemToPLayer(playerFlareData.player, playerFlareData.siteConfig.itemConfig, 1);
NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "MaxSitesForPlayer");
roadFlare.Kill();
return;
}
}
BuildingSite buildingSite = SiteSpawner.TrySpawnSite(this.transform.position, playerFlareData.siteConfig);
if (buildingSite == null) {
LootManager.GiveItemToPLayer(playerFlareData.player, playerFlareData.siteConfig.itemConfig, 1);
NotifyManagerLite.SendMessageToPlayer(playerFlareData.player, "Spawn_Failed");
}
else {
buildingSite.SetSiteOwner(playerFlareData.player.userID);
}
roadFlare.Kill();
}
}
class PlayerFlareInfo {
internal BasePlayer player;
internal BaseSiteConfig siteConfig;
internal PlayerFlareInfo(BasePlayer player, BaseSiteConfig siteConfig) {
this.player = player;
this.siteConfig = siteConfig;
}
}
static class SiteSpawner {
static Coroutine spawnCorountine;
internal static void StartAutoSpawn() {
if (!ins._config.groundTypeConfig.isAutoSpawn && !ins._config.waterTypeConfig.isAutoSpawn && !ins._config.skyTypeConfig.isAutoSpawn) return;
spawnCorountine = ServerMgr.Instance.StartCoroutine(AutoSpawnCoroutine());
}
static IEnumerator AutoSpawnCoroutine() {
NotifyManagerLite.PrintWarningMessage("SpawnStart_Log");
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);
yield return SpawnLocationCoroutine(siteConfig);
}
}
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);
yield return SpawnLocationCoroutine(siteConfig);
}
}
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);
yield return SpawnLocationCoroutine(siteConfig);
}
}
NotifyManagerLite.PrintWarningMessage("SpawnStop_Log");
yield return CoroutineEx.waitForSeconds(1);
}
static BaseSiteConfig GetRandomSiteConfig(IEnumerable<BaseSiteConfig> 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;
}
internal static void StopAutoSpawn() {
if (spawnCorountine != null) ServerMgr.Instance.StopCoroutine(spawnCorountine);
}
internal static void SpawnSiteInRandomLocation(string presetName) {
BaseSiteConfig baseSiteConfig = BuildingSite.GetSiteConfigByPresetName(presetName);
if (baseSiteConfig == null) {
NotifyManagerLite.PrintError(null, "ConfigNotFound_Exeption", presetName);
return;
}
BuildingSiteData siteData;
ins.siteCustomizationDatas.TryGetValue(baseSiteConfig.dataFileName, out siteData);
if (siteData == null) {
NotifyManagerLite.PrintError(null, "DataFileNotFound_Exeption", baseSiteConfig.dataFileName);
return;
}
ServerMgr.Instance.StartCoroutine(SpawnLocationCoroutine(baseSiteConfig));
}
static IEnumerator SpawnLocationCoroutine(BaseSiteConfig siteConfig) {
int counter = 5000;
bool isSpawned = false;
while (counter-- > 0) {
Vector3 position = PositionDefiner.GetRandomMapPoint();
if (TrySpawnSite(position, siteConfig) != null) isSpawned = true;
if (isSpawned) break;
else if (counter % 3 == 0) yield return CoroutineEx.waitForEndOfFrame;
}
if (!isSpawned) NotifyManagerLite.PrintError(null, $"FailedToSpawn {siteConfig.presetName}");
yield return CoroutineEx.waitForSeconds(3f);
}
internal static BuildingSite TrySpawnSite(Vector3 position, BaseSiteConfig siteConfig) {
HashSet<BaseEntity> entitiesForDestroy;
if (IsPositionAvilable(ref position, siteConfig, out entitiesForDestroy)) {
return SpawnSite(siteConfig.presetName, position, entitiesForDestroy);
}
return null;
}
internal static bool IsPositionAvilable(ref Vector3 position, BaseSiteConfig siteConfig, out HashSet<BaseEntity> entitiesForDestroy, bool ignorePlayers = false) {
entitiesForDestroy = new HashSet<BaseEntity>();
GroundSiteConfig groundSiteConfig = siteConfig as GroundSiteConfig;
if (groundSiteConfig != null) {
if (IsGroundPositionAvailable(position, groundSiteConfig, out entitiesForDestroy, ignorePlayers)) return true;
return false;
}
WaterSiteConfig waterSiteConfig = siteConfig as WaterSiteConfig;
if (waterSiteConfig != null) {
position.y = 0;
if (IsWaterPositionAvailable(position, waterSiteConfig, out entitiesForDestroy, ignorePlayers)) return true;
return false;
}
SkySiteConfig skySiteConfig = siteConfig as SkySiteConfig;
if (skySiteConfig != null) {
position.y = UnityEngine.Random.Range(ins._config.skyTypeConfig.minSpawnHeight, ins._config.skyTypeConfig.maxSpawnHeight);
if (IsSkyPositionAvailable(position, skySiteConfig, ignorePlayers)) return true;
return false;
}
return false;
}
static BuildingSite SpawnSite(string sitePresetName, Vector3 position, HashSet<BaseEntity> entitiesForDestroy) {
foreach (BaseEntity entity in entitiesForDestroy) if (entity.IsExists()) entity.Kill();
Quaternion randomRotation = Quaternion.Euler(0, UnityEngine.Random.Range(0, 360f), 0);
return BuildingSite.SpawnSite(sitePresetName, position, randomRotation);
}
static bool IsGroundPositionAvailable(Vector3 position, GroundSiteConfig groundSiteConfig, out HashSet<BaseEntity> entitiesForDestroy, bool ignorePlayers) {
entitiesForDestroy = new HashSet<BaseEntity>();
float mapHeigth = TerrainMeta.HeightMap.GetHeight(position);
if (mapHeigth < -ins._config.groundTypeConfig.maxDepht) return false;
if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, groundSiteConfig)) return false;
if (IsPositionBlockedByTopology(position, groundSiteConfig)) return false;
if (IsPositionBlockByHeigthInRadius(position, groundSiteConfig.insideRadius, groundSiteConfig.maxUpDeltaHeigh, groundSiteConfig.maxDownDeltaHeigh)) return false;
if (IsPositionBlockByHeigthInRadius(position, groundSiteConfig.insideRadius * 0.5f, groundSiteConfig.maxUpDeltaHeigh, groundSiteConfig.maxDownDeltaHeigh)) return false;
if (IsAnyEntityBlockSpawn(position, groundSiteConfig, out entitiesForDestroy, ignorePlayers)) return false;
return true;
}
static bool IsAnyEntityBlockSpawn(Vector3 position, BaseSiteConfig baseSiteConfig, out HashSet<BaseEntity> entitiesForDestroy, bool ignorePlayers) {
entitiesForDestroy = new HashSet<BaseEntity>();
foreach (Collider collider in UnityEngine.Physics.OverlapSphere(position, baseSiteConfig.outsideRadius)) {
if (collider.name.Contains("heatSource")) continue;
if (collider.name.Contains("Safe") || collider.name.Contains("Trigger (8)")) return true;
BaseEntity entity = collider.ToBaseEntity();
if (entity == null) {
string colliderLowerName = collider.name.ToLower();
if (colliderLowerName.Contains("prevent_building") || colliderLowerName.Contains("preventbuilding") || colliderLowerName.Contains("prevent building")) return true;
continue;
}
if (entity.GetBuildingPrivilege() != null) return true;
if (ignorePlayers && entity is BaseBoat && baseSiteConfig is WaterSiteConfig) continue;
if (entity is BuildingBlock or SimpleBuildingBlock or BaseVehicle) return true;
if (!ignorePlayers && entity is BasePlayer) return true;
if (entity is JunkPile or DiveSite or LootContainer or OreResourceEntity or BasePortal) entitiesForDestroy.Add(entity);
else if (ins._config.mainConfig.killTrees && entity is TreeEntity) entitiesForDestroy.Add(entity);
}
return false;
}
static bool IsPositionBlockedByTopology(Vector3 postition, BaseSiteConfig baseSiteConfig, int angleStep = 15) {
if (TopologyChecker.IsPositionBlockedByTopologies(postition, baseSiteConfig)) return true;
for (int angle = 0; angle < 360; angle += angleStep) {
float radian = 2f * Mathf.PI * angle / 360;
float x = postition.x + baseSiteConfig.outsideRadius * Mathf.Cos(radian);
float z = postition.z + baseSiteConfig.outsideRadius * Mathf.Sin(radian);
Vector3 positionInRadius = PositionDefiner.GetGroundPosition(new Vector3(x, postition.y, z));
if (TopologyChecker.IsPositionBlockedByTopologies(positionInRadius, baseSiteConfig)) return true;
if (baseSiteConfig is WaterSiteConfig && !TopologyChecker.IsOceanTopology(positionInRadius)) return true;
}
return false;
}
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));
float delta = positionInRadius.y - postition.y;
if (delta > 0 && delta > maxUpDelta) return true;
if (delta < 0 && -delta > maxDownDelta) return true;
}
return false;
}
static bool IsWaterPositionAvailable(Vector3 position, WaterSiteConfig waterSiteConfig, out HashSet<BaseEntity> entitiesForDestroy, bool ignorePlayers) {
entitiesForDestroy = new HashSet<BaseEntity>();
float mapHeigth = TerrainMeta.HeightMap.GetHeight(position);
if (-mapHeigth < ins._config.waterTypeConfig.minDepth) return false;
if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, waterSiteConfig)) return false;
if (GetShoreDistance(position) > ins._config.waterTypeConfig.maxShoreDistance) return false;
if (!IsPositionInMapBounds(position, waterSiteConfig)) return false;
if (IsPositionBlockedByTopology(position, waterSiteConfig)) return false;
if (PositionDefiner.IsPositionOnCargoPath(position)) return false;
if (IsAnyEntityBlockSpawn(position, waterSiteConfig, out entitiesForDestroy, ignorePlayers)) return false;
return true;
}
static float GetShoreDistance(Vector3 position) {
float xDistanceToShore = position.x - World.Size / 2;
float zDistanceToShore = position.z - World.Size / 2;
float distanceToShore = xDistanceToShore > zDistanceToShore ? xDistanceToShore : zDistanceToShore;
//Distance to shore = 32339
return distanceToShore;
}
static bool IsSkyPositionAvailable(Vector3 position, SkySiteConfig skySiteConfig, bool ignorePlayers) {
HashSet<BaseEntity> entitiesForDestroy = new HashSet<BaseEntity>();
Vector3 groundPosition = PositionDefiner.GetGroundPosition(position);
float distanceToGround = position.y - groundPosition.y;
if (distanceToGround < ins._config.skyTypeConfig.minSpawnHeight / 2.5f) return false;
if (BuildingSite.IsSpawnPositionBlockByOtherSite(position, skySiteConfig)) return false;
if (!IsPositionInMapBounds(position, skySiteConfig)) return false;
if (IsAnyEntityBlockSpawn(position, skySiteConfig, out entitiesForDestroy, ignorePlayers)) return false;
return true;
}
static bool IsPositionInMapBounds(Vector3 position, BaseSiteConfig baseSiteConfig) {
if (Mathf.Abs(position.x) + baseSiteConfig.outsideRadius > World.Size / 2) return false;
if (Mathf.Abs(position.z) + baseSiteConfig.outsideRadius > World.Size / 2) return false;
return true;
}
}
static class TopologyChecker {
const int oceanTopologies = (int)(TerrainTopology.Enum.Ocean | TerrainTopology.Enum.Oceanside);
const int blockedTopologies = (int)(TerrainTopology.Enum.Building | TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside | TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside);
const int monumentTopologies = (int)(TerrainTopology.Enum.Monument | TerrainTopology.Enum.Building);
const int beachTopologies = (int)(TerrainTopology.Enum.Beach | TerrainTopology.Enum.Beachside);
const int riverTopologies = (int)(TerrainTopology.Enum.River | TerrainTopology.Enum.Riverside | TerrainTopology.Enum.Lake | TerrainTopology.Enum.Lakeside);
internal static bool IsPositionBlockedByTopologies(Vector3 postition, BaseSiteConfig baseSiteConfig) {
if (TopologyChecker.IsBlockedTopology(postition)) return true;
if (!ins._config.groundTypeConfig.isRiverSpawnEnabled && TopologyChecker.IsRiverOrLakeTopology(postition)) return true;
if (baseSiteConfig is GroundSiteConfig && !ins._config.groundTypeConfig.isBeachSpawnEnabled && (TopologyChecker.IsBeachTopology(postition) || TopologyChecker.IsOceanTopology(postition))) return true;
return false;
}
static bool IsBlockedTopology(Vector3 position) {
int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position);
if ((pointTopologies & blockedTopologies) != 0) return true;
if ((pointTopologies & monumentTopologies) != 0) return true;
return false;
}
static bool IsBeachTopology(Vector3 position) {
int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position);
if ((pointTopologies & beachTopologies) != 0) return true;
return false;
}
static bool IsRiverOrLakeTopology(Vector3 position) {
int pointTopologies = TerrainMeta.TopologyMap.GetTopology(position);
if ((pointTopologies & riverTopologies) != 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<BuildingSite> buildingSites = new HashSet<BuildingSite>();
internal BaseSiteConfig siteConfig;
BaseEntity mainEntity;
BuildingSiteData siteData;
MapMarker mapMarker;
HashSet<GameObject> shoudDestroyAfterUnload = new HashSet<GameObject>();
internal static void Initialize() {
LoadSites();
if (buildingSites.Count == 0) SiteSpawner.StartAutoSpawn();
}
static void LoadSites() {
HashSet<DoorCloser> doorClosers = BaseNetworkable.serverEntities.OfType<DoorCloser>();
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.itemConfig.skin == skinID);
if (result == null) result = ins._config.waterTypeConfig.sites.FirstOrDefault(x => x.itemConfig.skin == skinID);
if (result == null) result = ins._config.skyTypeConfig.sites.FirstOrDefault(x => x.itemConfig.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<ConfigType>(Vector3 position, out float minDistance) where ConfigType : BaseSiteConfig {
minDistance = float.MaxValue;
BuildingSite result = null;
foreach (BuildingSite buildingSite in buildingSites) {
if (buildingSite == null || buildingSite.siteConfig is not ConfigType) 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<BuildingSite> GetPlayersSites(ulong userID) {
return buildingSites.Where(x => x != null && x.mainEntity.IsExists() && x.mainEntity.OwnerID == userID);
}
internal static bool IsSpawnPositionBlockByOtherSite(Vector3 position, BaseSiteConfig siteConfig) {
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 < (siteConfig.outsideRadius + site.siteConfig.outsideRadius) * 1.5f) 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.itemConfig.skin, true);
BuildingSite buildingSite = TryAttachBuildingSiteClass(mainEntity, presetName, true);
if (buildingSite == null) mainEntity.Kill();
return buildingSite;
}
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<BuildingSite>();
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 Pe
// ... [Code preview truncated due to length. Full source in attached file] ...