C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEngine;
using Oxide.Game.Rust.Cui;
using System.Reflection;
namespace Oxide.Plugins {
[Info("ZoneManager", "Reneb / Nogrod", "2.5.09", ResourceId = 739)]
public class ZoneManager : RustPlugin {
#region Fields
private DynamicConfigFile ZoneManagerData;
private StoredData storedData;
[PluginReference]
Plugin PopupNotifications, Spawns;
private static ZoneManager instance;
private ZoneFlags disabledFlags = ZoneFlags.None;
private Hash<string, Zone> zoneObjects = new Hash<string, Zone>();
private readonly Dictionary<string, Zone.Definition> zoneDefinitions = new Dictionary<string, Zone.Definition>();
private readonly Dictionary<ulong, string> lastPlayerZone = new Dictionary<ulong, string>();
private readonly Dictionary<BasePlayer, HashSet<Zone>> playerZones = new Dictionary<BasePlayer, HashSet<Zone>>();
private readonly Dictionary<BaseCombatEntity, HashSet<Zone>> buildingZones = new Dictionary<BaseCombatEntity, HashSet<Zone>>();
private readonly Dictionary<BaseNpc, HashSet<Zone>> npcZones = new Dictionary<BaseNpc, HashSet<Zone>>();
private readonly Dictionary<ResourceDispenser, HashSet<Zone>> resourceZones = new Dictionary<ResourceDispenser, HashSet<Zone>>();
private readonly Dictionary<BaseEntity, HashSet<Zone>> otherZones = new Dictionary<BaseEntity, HashSet<Zone>>();
private readonly Dictionary<BasePlayer, ZoneFlags> playerTags = new Dictionary<BasePlayer, ZoneFlags>();
private static readonly int playersMask = 131072;
private static readonly Collider[] colBuffer = Vis.colBuffer;
private static FieldInfo SearchLight_secondsRemaining;
private const string permZone = "zonemanager.zone";
private const string permIgnoreFlag = "zonemanager.ignoreflag.";
#endregion
#region Oxide Hooks
private void Loaded() {
instance = this;
lang.RegisterMessages(Messages, this);
ZoneManagerData = Interface.Oxide.DataFileSystem.GetFile("ZoneManager");
ZoneManagerData.Settings.Converters = new JsonConverter[] {
new StringEnumConverter(), new UnityVector3Converter(),
}
;
permission.RegisterPermission(permZone, this);
foreach (ZoneFlags flag in Enum.GetValues(typeof(ZoneFlags))) permission.RegisterPermission(permIgnoreFlag + flag.ToString().ToLower(), this);
SearchLight_secondsRemaining = typeof(SearchLight).GetField("secondsRemaining", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
LoadVariables();
LoadData();
}
private void OnServerInitialized() => InitializeZones();
private void Unload() {
foreach (BasePlayer player in BasePlayer.activePlayerList) CuiHelper.DestroyUi(player, ZMUI);
for (int i = zoneObjects.Count - 1; i >= 0; i--) {
var zone = zoneObjects.ElementAt(i);
OnZoneDestroy(zone.Value);
UnityEngine.Object.Destroy(zone.Value);
zoneObjects.Remove(zone.Key);
}
instance = null;
}
private void OnTerrainInitialized() => InitializeZones();
private void InitializeZones() {
if (Initialized) return;
foreach (Zone.Definition zoneDefinition in zoneDefinitions.Values) CreateNewZone(zoneDefinition);
Initialized = true;
}
private void OnEntityBuilt(Planner planner, GameObject gameobject) {
BasePlayer player = planner?.GetOwnerPlayer();
if (player == null) return;
BaseEntity entity = gameobject.ToBaseEntity();
if (entity == null) return;
if (entity is BuildingBlock || entity is SimpleBuildingBlock) {
if (HasPlayerFlag(player, ZoneFlags.NoBuild, true)) {
entity.Kill(BaseNetworkable.DestroyMode.Gib);
SendMessage(player, msg("noBuild", player.UserIDString));
}
}
else {
if (entity is BuildingPrivlidge) {
if (HasPlayerFlag(player, ZoneFlags.NoCup, false)) {
entity.Kill(BaseNetworkable.DestroyMode.Gib);
SendMessage(player, msg("noCup", player.UserIDString));
}
}
else {
if (HasPlayerFlag(player, ZoneFlags.NoDeploy, true)) {
entity.Kill(BaseNetworkable.DestroyMode.Gib);
SendMessage(player, msg("noDeploy", player.UserIDString));
}
}
}
}
private object OnStructureUpgrade(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum grade) {
if (HasPlayerFlag(player, ZoneFlags.NoUpgrade, true)) {
SendMessage(player, msg("noUpgrade", player.UserIDString));
return false;
}
return null;
}
private void OnItemDeployed(Deployer deployer, BaseEntity deployedEntity) {
BasePlayer player = deployer.GetOwnerPlayer();
if (player == null) return;
if (HasPlayerFlag(player, ZoneFlags.NoDeploy, true)) {
deployedEntity.Kill(BaseNetworkable.DestroyMode.Gib);
SendMessage(player, msg("noDeploy", player.UserIDString));
}
}
private void OnRunPlayerMetabolism(PlayerMetabolism metabolism, BaseCombatEntity ownerEntity, float delta) {
BasePlayer player = ownerEntity as BasePlayer;
if (player == null) return;
if (metabolism.bleeding.value > 0 && HasPlayerFlag(player, ZoneFlags.NoBleed, false)) metabolism.bleeding.value = 0f;
if (metabolism.oxygen.value < 1 && HasPlayerFlag(player, ZoneFlags.NoDrown, false)) metabolism.oxygen.value = 1;
}
private object OnPlayerChat(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null) return null;
if (HasPlayerFlag(player, ZoneFlags.NoChat, true)) {
SendMessage(player, msg("noChat", player.UserIDString));
return false;
}
return null;
}
private object OnServerCommand(ConsoleSystem.Arg arg) {
BasePlayer player = arg.Player();
if (player == null || arg.cmd?.Name == null) return null;
if (arg.cmd.Name == "kill" && HasPlayerFlag(player, ZoneFlags.NoSuicide, false)) {
SendMessage(player, msg("noSuicide", player.UserIDString));
return false;
}
return null;
}
private void OnPlayerDisconnected(BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.KillSleepers, true)) {
player.Die();
return;
}
if (HasPlayerFlag(player, ZoneFlags.EjectSleepers, true)) {
HashSet<Zone> zones;
if (!playerZones.TryGetValue(player, out zones) || zones.Count == 0) return;
foreach (Zone zone in zones) {
if (HasZoneFlag(zone, ZoneFlags.EjectSleepers)) {
EjectPlayer(zone, player);
return;
}
}
}
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo) {
if (entity == null || entity.GetComponent<ResourceDispenser>() != null) return;
BasePlayer attacker = hitinfo.InitiatorPlayer;
BasePlayer victim = entity as BasePlayer;
if (victim != null) {
if (victim.IsSleeping() && HasPlayerFlag(victim, ZoneFlags.SleepGod, false)) CancelDamage(hitinfo);
else if (attacker != null) {
if (attacker.userID < 76560000000000000L) return;
if (HasPlayerFlag(victim, ZoneFlags.PvpGod, false)) CancelDamage(hitinfo);
else if (HasPlayerFlag(attacker, ZoneFlags.PvpGod, false)) CancelDamage(hitinfo);
}
else if (HasPlayerFlag(victim, ZoneFlags.PveGod, false)) CancelDamage(hitinfo);
else if (hitinfo.Initiator is FireBall && HasPlayerFlag(victim, ZoneFlags.PvpGod, false)) CancelDamage(hitinfo);
return;
}
BaseNpc baseNpc = entity as BaseNpc;
if (baseNpc != null) {
HashSet<Zone> zones;
if (!npcZones.TryGetValue(baseNpc, out zones)) return;
foreach (var zone in zones) {
if (HasZoneFlag(zone, ZoneFlags.NoPve)) {
if (hitinfo.InitiatorPlayer != null && CanBypass(hitinfo.InitiatorPlayer, ZoneFlags.NoPve)) continue;
CancelDamage(hitinfo);
break;
}
}
return;
}
if (!(entity is LootContainer) && !(entity is BaseHelicopter)) {
ResourceDispenser resource = entity.GetComponent<ResourceDispenser>();
HashSet<Zone> zones;
if (!buildingZones.TryGetValue(entity, out zones) && (resource == null || !resourceZones.TryGetValue(resource, out zones))) return;
foreach (Zone zone in zones) {
if (HasZoneFlag(zone, ZoneFlags.UnDestr)) {
if (hitinfo.InitiatorPlayer != null && CanBypass(hitinfo.InitiatorPlayer, ZoneFlags.UnDestr)) continue;
CancelDamage(hitinfo);
break;
}
}
}
}
private void OnEntityKill(BaseNetworkable networkable) {
BaseEntity entity = networkable as BaseEntity;
if (entity == null) return;
ResourceDispenser resource = entity.GetComponent<ResourceDispenser>();
if (resource != null) {
HashSet<Zone> zones;
if (resourceZones.TryGetValue(resource, out zones)) OnResourceExitZone(null, resource, true);
return;
}
BasePlayer player = entity as BasePlayer;
if (player != null) {
HashSet<Zone> zones;
if (playerZones.TryGetValue(player, out zones)) OnPlayerExitZone(null, player, true);
return;
}
BaseNpc npc = entity as BaseNpc;
if (npc != null) {
HashSet<Zone> zones;
if (npcZones.TryGetValue(npc, out zones)) OnNpcExitZone(null, npc, true);
return;
}
BaseCombatEntity building = entity as BaseCombatEntity;
if (building != null && !(entity is LootContainer) && !(entity is BaseHelicopter)) {
HashSet<Zone> zones;
if (buildingZones.TryGetValue(building, out zones)) OnBuildingExitZone(null, building, true);
}
else {
HashSet<Zone> zones;
if (otherZones.TryGetValue(entity, out zones)) OnOtherExitZone(null, entity, true);
}
}
private void OnEntitySpawned(BaseNetworkable entity) {
if (entity is BaseCorpse) {
timer.Once(2f, () => {
HashSet<Zone> zones;
if (entity == null || entity.IsDestroyed || !entity.GetComponent<ResourceDispenser>() || !resourceZones.TryGetValue(entity.GetComponent<ResourceDispenser>(), out zones)) return;
foreach (Zone zone in zones) {
if (HasZoneFlag(zone, ZoneFlags.NoCorpse) && !CanBypass((entity as BaseCorpse).OwnerID, ZoneFlags.NoCorpse)) {
entity.Kill(BaseNetworkable.DestroyMode.None);
break;
}
}
}
);
}
else if (entity is BuildingBlock && zoneObjects != null) {
BuildingBlock block = (BuildingBlock)entity;
if (EntityHasFlag(entity as BuildingBlock, ZoneFlags.NoStability) && !CanBypass((entity as BuildingBlock).OwnerID, ZoneFlags.NoStability)) block.grounded = true;
}
else if (entity is LootContainer || entity is JunkPile || entity is BaseNpc || entity is WorldItem || entity is DroppedItem || entity is DroppedItemContainer) timer.In(2, () => CheckSpawnedEntity(entity));
}
private object CanBeWounded(BasePlayer player, HitInfo hitinfo) => HasPlayerFlag(player, ZoneFlags.NoWounded, false) ? (object)false : null;
private object CanUpdateSign(BasePlayer player, Signage sign) {
if (HasPlayerFlag(player, ZoneFlags.NoSignUpdates, false)) {
SendMessage(player, msg("noSignUpdates", player.UserIDString));
return false;
}
return null;
}
private object OnOvenToggle(BaseOven oven, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoOvenToggle, false)) {
SendMessage(player, msg("noOvenToggle", player.UserIDString));
return false;
}
return null;
}
private object CanUseVending(VendingMachine machine, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoVending, false)) {
SendMessage(player, msg("noVending", player.UserIDString));
return false;
}
return null;
}
private object CanHideStash(StashContainer stash, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoStash, false)) {
SendMessage(player, msg("noStash", player.UserIDString));
return false;
}
return null;
}
private object CanCraft(ItemCrafter itemCrafter, ItemBlueprint bp, int amount) {
BasePlayer player = itemCrafter.GetComponent<BasePlayer>();
if (player != null) {
if (HasPlayerFlag(player, ZoneFlags.NoCraft, false)) {
SendMessage(player, msg("noCraft", player.UserIDString));
return false;
}
}
return null;
}
private object CanMountEntity(BasePlayer player, BaseMountable mount) {
if (player != null) {
if (HasPlayerFlag(player, ZoneFlags.NoMount, false)) {
SendMessage(player, msg("noMount", player.UserIDString));
return false;
}
}
return null;
}
#region Looting Hooks
private object CanLootPlayer(BasePlayer target, BasePlayer looter) => OnLootPlayerInternal(looter, target);
private void OnLootPlayer(BasePlayer looter, BasePlayer target) => OnLootPlayerInternal(looter, target);
private object OnLootPlayerInternal(BasePlayer looter, BasePlayer target) {
if (HasPlayerFlag(looter, ZoneFlags.NoPlayerLoot, false) || (target != null && HasPlayerFlag(target, ZoneFlags.NoPlayerLoot, false))) {
SendMessage(looter, msg("noLoot", looter.UserIDString));
NextTick(looter.EndLooting);
return false;
}
return null;
}
private void OnLootEntity(BasePlayer player, BaseEntity entity) {
if (entity is DroppedItemContainer || entity is LootableCorpse) OnLootInternal(player, ZoneFlags.NoPlayerLoot);
if (entity is StorageContainer) OnLootInternal(player, ZoneFlags.NoBoxLoot);
}
private object CanLootEntity(LootableCorpse corpse, BasePlayer player) => CanLootInternal(player, ZoneFlags.NoPlayerLoot);
private object CanLootEntity(DroppedItemContainer container, BasePlayer player) => CanLootInternal(player, ZoneFlags.NoPlayerLoot);
private object CanLootEntity(StorageContainer container, BasePlayer player) => CanLootInternal(player, ZoneFlags.NoBoxLoot);
private object CanLootInternal(BasePlayer player, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
SendMessage(player, msg("noLoot", player.UserIDString));
return false;
}
return null;
}
private void OnLootInternal(BasePlayer player, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
SendMessage(player, msg("noLoot", player.UserIDString));
NextTick(player.EndLooting);
}
}
#endregion
#region Pickup Hooks
private object CanPickupEntity(BaseCombatEntity entity, BasePlayer player) => CanPickupInternal(player, ZoneFlags.NoEntityPickup);
private object CanPickupLock(BasePlayer player, BaseLock baseLock) => CanPickupInternal(player, ZoneFlags.NoEntityPickup);
private object OnItemPickup(Item item, BasePlayer player) => CanPickupInternal(player, ZoneFlags.NoPickup);
private object CanPickupInternal(BasePlayer player, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
SendMessage(player, msg("noPickup", player.UserIDString));
return false;
}
return null;
}
#endregion
#region Gather Hooks
private object CanLootEntity(ResourceContainer container, BasePlayer player) => OnGatherInternal(player);
private object OnCollectiblePickup(Item item, BasePlayer player) => OnGatherInternal(player);
private object OnCropGather(PlantEntity plant, Item item, BasePlayer player) => OnGatherInternal(player);
private object OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) => OnGatherInternal(entity?.ToPlayer());
private object OnGatherInternal(BasePlayer player) {
if (player != null) {
if (HasPlayerFlag(player, ZoneFlags.NoGather, false)) {
SendMessage(player, msg("noGather", player.UserIDString));
return false;
}
}
return null;
}
#endregion
#region Targeting Hooks
private object OnTurretTarget(AutoTurret turret, BaseCombatEntity entity) => OnTargetPlayerInternal(entity?.ToPlayer(), ZoneFlags.NoTurretTargeting);
private object CanBradleyApcTarget(BradleyAPC apc, BaseEntity entity) => OnTargetPlayerInternal(entity?.ToPlayer(), ZoneFlags.NoAPCTargeting);
private object CanHelicopterTarget(PatrolHelicopterAI heli, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoHeliTargeting, false)) {
heli.interestZoneOrigin = heli.GetRandomPatrolDestination();
return false;
}
return null;
}
private object CanHelicopterStrafeTarget(PatrolHelicopterAI heli, BasePlayer player) => OnTargetPlayerInternal(player, ZoneFlags.NoHeliTargeting);
private object OnHelicopterTarget(HelicopterTurret turret, BaseCombatEntity entity) => OnTargetPlayerInternal(entity?.ToPlayer(), ZoneFlags.NoHeliTargeting);
private object OnNpcPlayerTarget(NPCPlayerApex npcPlayer, BaseEntity entity) => OnTargetPlayerInternal(entity?.ToPlayer(), ZoneFlags.NoNPCTargeting);
private object OnTargetPlayerInternal(BasePlayer player, ZoneFlags flag) {
if (player != null) {
if (HasPlayerFlag(player, flag, false)) return false;
}
return null;
}
#endregion
#region Additional KillSleeper Checks
private void OnPlayerSleep(BasePlayer player) {
if (player == null) return;
player.Invoke(() => KillSleepingPlayer(player), 5f);
}
private void KillSleepingPlayer(BasePlayer player) {
if (player == null || !player.IsSleeping()) return;
if (HasPlayerFlag(player, ZoneFlags.KillSleepers, true)) {
if (player.IsConnected) OnPlayerSleep(player);
else player.Die();
}
}
#endregion
#endregion
#region Zone Management
#region Zone Component
public class Zone : MonoBehaviour {
public Definition definition;
public ZoneFlags disabledFlags = ZoneFlags.None;
public readonly HashSet<ulong> WhiteList = new HashSet<ulong>();
public readonly HashSet<ulong> KeepInList = new HashSet<ulong>();
private HashSet<BasePlayer> players = new HashSet<BasePlayer>();
private HashSet<BaseCombatEntity> buildings = new HashSet<BaseCombatEntity>();
private List<TriggerBase> triggers = new List<TriggerBase>();
private bool lightsOn;
#region Initialization
private void Awake() {
gameObject.layer = (int)Layer.Reserved1;
gameObject.name = "Zone Manager";
enabled = false;
Rigidbody rigidbody = gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = false;
rigidbody.isKinematic = true;
rigidbody.detectCollisions = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
}
private void OnDestroy() {
Cleanup();
if (instance != null) instance.OnZoneDestroy(this);
Destroy(gameObject);
}
private void Cleanup() {
if (IsInvoking(CheckEntities)) CancelInvoke(CheckEntities);
if (IsInvoking(CheckAlwaysLights)) CancelInvoke(CheckAlwaysLights);
if (IsInvoking(CheckLights)) CancelInvoke(CheckLights);
foreach (TriggerBase trigger in triggers) {
if (trigger.gameObject.name.StartsWith("Trigger")) Destroy(trigger.gameObject);
else Destroy(trigger);
}
triggers.Clear();
}
public void SetInfo(Definition definition) {
this.definition = definition;
Cleanup();
InitializeZone(definition.Enabled);
}
public void InitializeZone(bool active) {
if (definition == null) return;
gameObject.name = $"Zone Manager({definition.Id})";
transform.position = definition.Location;
transform.rotation = Quaternion.Euler(definition.Rotation);
UpdateCollider();
RegisterPermission();
InitializeAutoLights();
InitializeRadiation();
InitializeComfort();
InitializeTemperature();
SetZoneStatus(active);
}
private void UpdateCollider() {
SphereCollider sphereCollider = gameObject.GetComponent<SphereCollider>();
BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
if (definition.Size != Vector3.zero) {
if (sphereCollider != null) Destroy(sphereCollider);
if (boxCollider == null) {
boxCollider = gameObject.AddComponent<BoxCollider>();
boxCollider.isTrigger = true;
}
boxCollider.size = definition.Size;
}
else {
if (boxCollider != null) Destroy(boxCollider);
if (sphereCollider == null) {
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.isTrigger = true;
}
sphereCollider.radius = definition.Radius;
}
}
private void RegisterPermission() {
if (!string.IsNullOrEmpty(definition.Permission) && !instance.permission.PermissionExists(definition.Permission)) instance.permission.RegisterPermission(definition.Permission, instance);
}
private void InitializeAutoLights() {
if (HasZoneFlag(ZoneFlags.AlwaysLights)) {
if (IsInvoking(CheckAlwaysLights)) CancelInvoke(CheckAlwaysLights);
InvokeRepeating(CheckAlwaysLights, 5f, 60f);
}
else if (HasZoneFlag(ZoneFlags.AutoLights)) {
float currentTime = GetSkyHour();
if (currentTime > instance.AutolightOffTime && currentTime < instance.AutolightOnTime) lightsOn = true;
else lightsOn = false;
if (IsInvoking(CheckLights)) CancelInvoke(CheckLights);
InvokeRepeating(CheckLights, 5f, 30f);
}
}
#region Create Effect Triggers
private void InitializeRadiation() {
if (definition.Radiation > 0) {
GameObject radiationObject = gameObject;
if (definition.Size != Vector3.zero) {
radiationObject = new GameObject();
radiationObject.name = $"Trigger Radiation {definition.Id}";
radiationObject.transform.position = gameObject.transform.position;
SphereCollider collider = radiationObject.AddComponent<SphereCollider>();
collider.radius = definition.Radius;
collider.isTrigger = true;
}
TriggerRadiation radiation = radiationObject.GetComponent<TriggerRadiation>() ?? radiationObject.AddComponent<TriggerRadiation>();
radiation.RadiationAmountOverride = definition.Radiation;
radiation.radiationSize = definition.Radius;
radiation.interestLayers = playersMask;
radiation.enabled = true;
if (!triggers.Contains(radiation)) triggers.Add(radiation);
}
}
private void InitializeComfort() {
if (definition.Comfort > 0) {
GameObject comfortObject = gameObject;
if (definition.Size != Vector3.zero) {
comfortObject = new GameObject();
comfortObject.name = $"Trigger Comfort {definition.Id}";
comfortObject.transform.position = gameObject.transform.position;
SphereCollider collider = comfortObject.AddComponent<SphereCollider>();
collider.radius = definition.Radius;
collider.isTrigger = true;
}
TriggerComfort comfort = comfortObject.GetComponent<TriggerComfort>() ?? comfortObject.AddComponent<TriggerComfort>();
comfort.baseComfort = definition.Comfort;
comfort.triggerSize = definition.Radius;
comfort.interestLayers = playersMask;
comfort.enabled = true;
if (!triggers.Contains(comfort)) triggers.Add(comfort);
}
}
private void InitializeTemperature() {
if (definition.Temperature != 0) {
GameObject temperatureObject = gameObject;
if (definition.Size != Vector3.zero) {
temperatureObject = new GameObject();
temperatureObject.name = $"Trigger Temperature {definition.Id}";
temperatureObject.transform.position = gameObject.transform.position;
SphereCollider collider = temperatureObject.AddComponent<SphereCollider>();
collider.radius = definition.Radius;
collider.isTrigger = true;
}
TriggerTemperature temperature = temperatureObject.GetComponent<TriggerTemperature>() ?? temperatureObject.AddComponent<TriggerTemperature>();
temperature.Temperature = definition.Temperature;
temperature.triggerSize = definition.Radius;
temperature.interestLayers = playersMask;
temperature.enabled = true;
if (!triggers.Contains(temperature)) triggers.Add(temperature);
}
}
#endregion
private void SetZoneStatus(bool active) {
if (active) {
if (!IsInvoking(CheckEntities)) InvokeRandomized(CheckEntities, 1f, 5f, 1f);
gameObject.SetActive(true);
}
else {
if (IsInvoking(CheckEntities)) CancelInvoke(CheckEntities);
if (IsInvoking(CheckAlwaysLights)) CancelInvoke(CheckAlwaysLights);
if (IsInvoking(CheckLights)) CancelInvoke(CheckLights);
foreach (TriggerBase trigger in triggers) {
foreach (BasePlayer player in players) player.LeaveTrigger(trigger);
Destroy(trigger);
}
triggers.Clear();
gameObject.SetActive(false);
}
}
#endregion
#region InvokeHandler
private bool IsInvoking(Action action) => InvokeHandler.IsInvoking(this, action);
private void Invoke(Action action, float time) => InvokeHandler.Invoke(this, action, time);
private void InvokeRepeating(Action action, float time, float repeat) => InvokeHandler.InvokeRepeating(this, action, time, repeat);
private void InvokeRandomized(Action action, float time, float repeat, float random) => InvokeHandler.InvokeRandomized(this, action, time, repeat, random);
private void CancelInvoke(Action action) => InvokeHandler.CancelInvoke(this, action);
#endregion
#region Flag Monitoring
private bool HasZoneFlag(ZoneFlags flag) => ((disabledFlags & flag) == flag) ? false : (definition.Flags & ~disabledFlags & flag) == flag;
private void CheckLights() {
float currentTime = GetSkyHour();
if (currentTime > instance.AutolightOffTime && currentTime < instance.AutolightOnTime) {
if (!lightsOn) return;
foreach (var building in buildings) {
SearchLight searchLight = building as SearchLight;
if (searchLight != null) {
SearchLight_secondsRemaining.SetValue(searchLight, 0f);
searchLight.SetFlag(BaseEntity.Flags.On, false, false);
continue;
}
BaseOven oven = building as BaseOven;
if (oven != null && !oven.IsInvoking(oven.Cook)) {
oven.SetFlag(BaseEntity.Flags.On, false);
continue;
}
Door door = building as Door;
if (door != null && door.PrefabName.Contains("shutter")) door.SetFlag(BaseEntity.Flags.Open, true);
}
foreach (var player in players) {
if (player.userID >= 76560000000000000L || player.inventory?.containerWear?.itemList == null) continue;
List<Item> items = player.inventory.containerWear.itemList;
foreach (var item in items) {
if (!item.info.shortname.Equals("hat.miner") && !item.info.shortname.Equals("hat.candle")) continue;
item.SwitchOnOff(false, player);
player.inventory.ServerUpdate(0f);
break;
}
}
lightsOn = false;
}
else {
if (lightsOn) return;
foreach (var building in buildings) {
SearchLight searchLight = building as SearchLight;
if (searchLight != null) {
SearchLight_secondsRemaining.SetValue(searchLight, 9999999999f);
searchLight.SetFlag(BaseEntity.Flags.On, true, false);
continue;
}
BaseOven oven = building as BaseOven;
if (oven != null && !oven.IsInvoking(oven.Cook)) {
oven.SetFlag(BaseEntity.Flags.On, true);
continue;
}
Door door = building as Door;
if (door != null && door.PrefabName.Contains("shutter")) door.SetFlag(BaseEntity.Flags.Open, false);
}
ItemDefinition fuel = ItemManager.FindItemDefinition("lowgradefuel");
foreach (BasePlayer player in players) {
if (player.userID >= 76560000000000000L || player.inventory?.containerWear?.itemList == null) continue;
// only npc
List<Item> items = player.inventory.containerWear.itemList;
foreach (var item in items) {
if (!item.info.shortname.Equals("hat.miner") && !item.info.shortname.Equals("hat.candle")) continue;
if (item.contents == null) item.contents = new ItemContainer();
Item[] array = item.contents.itemList.ToArray();
for (var i = 0; i < array.Length; i++) array[i].Remove(0f);
Item newItem = ItemManager.Create(fuel, 100);
newItem.MoveToContainer(item.contents);
item.SwitchOnOff(true, player);
player.inventory.ServerUpdate(0f);
break;
}
}
lightsOn = true;
}
}
private void CheckAlwaysLights() {
foreach (var building in buildings) {
BaseOven oven = building as BaseOven;
if (oven == null || oven.IsInvoking(oven.Cook)) continue;
oven.SetFlag(BaseEntity.Flags.On, true);
}
}
public void OnEntityKill(BaseCombatEntity entity) {
BasePlayer player = entity as BasePlayer;
if (player != null) players.Remove(player);
else if (entity != null && !(entity is LootContainer) && !(entity is BaseHelicopter) && !(entity is BaseNpc)) buildings.Remove(entity);
}
#endregion
#region Trigger Detection
// Need something better then this, it is required to detect when a player is teleported out of the zone and put to sleep since the BasePlayer.Sleep() method destroys the players RigidBody thus making the players exit from the zone undetectable by any trigger/collision messages...private void CheckEntities()
{
HashSet<BasePlayer> oldPlayers = players;
players = new HashSet<BasePlayer>();
int entities = definition.Size != Vector3.zero ? Physics.OverlapBoxNonAlloc(definition.Location, definition.Size / 2, colBuffer, Quaternion.Euler(definition.Rotation), playersMask) : Physics.OverlapSphereNonAlloc(definition.Location, definition.Radius, colBuffer, playersMask);
for (var i = 0; i < entities; i++) {
BasePlayer player = colBuffer[i].GetComponentInParent<BasePlayer>();
colBuffer[i] = null;
if (player != null) {
if (players.Add(player) && !oldPlayers.Contains(player)) instance.OnPlayerEnterZone(this, player);
}
}
foreach (BasePlayer player in oldPlayers) {
if (!players.Contains(player)) instance.OnPlayerExitZone(this, player);
}
}
private void OnTriggerEnter(Collider col) {
BasePlayer player = col.GetComponent<BasePlayer>();
if (player != null) {
OnPlayerEnter(player);
return;
}
else if (!col.transform.CompareTag("MeshColliderBatch")) OnColliderEnterZone(col);
else {
MeshColliderBatch colliderBatch = col.GetComponent<MeshColliderBatch>();
if (colliderBatch == null) return;
var colliders = colliderBatch.meshLookup.src.data;
Bounds bounds = gameObject.GetComponent<Collider>().bounds;
foreach (var instance in colliders) {
if (instance.collider && instance.collider.bounds.Intersects(bounds)) OnColliderEnterZone(instance.collider);
}
}
}
private void OnColliderEnterZone(Collider col) {
if (HasZoneFlag(ZoneFlags.NoDecay)) {
DecayEntity decayEntity = col.GetComponentInParent<DecayEntity>();
if (decayEntity != null) decayEntity.decay = null;
}
ResourceDispenser resourceDispenser = col.GetComponentInParent<ResourceDispenser>();
if (resourceDispenser != null) {
instance.OnResourceEnterZone(this, resourceDispenser);
return;
}
BaseEntity entity = col.GetComponentInParent<BaseEntity>();
if (entity == null) return;
BaseNpc npc = entity as BaseNpc;
if (npc != null) {
instance.OnNpcEnterZone(this, npc);
return;
}
BaseCombatEntity combatEntity = entity as BaseCombatEntity;
if (combatEntity != null && !(entity is LootContainer) && !(entity is BaseHelicopter)) {
buildings.Add(combatEntity);
instance.OnBuildingEnterZone(this, combatEntity);
}
else instance.OnOtherEnterZone(this, entity);
}
private void OnTriggerExit(Collider col) {
BasePlayer player = col.GetComponent<BasePlayer>();
if (player != null) {
OnPlayerExit(player);
return;
}
else if(!col.transform.CompareTag("MeshColliderBatch")) OnColliderExitZone(col);
else {
MeshColliderBatch colliderBatch = col.GetComponent<MeshColliderBatch>();
if (colliderBatch == null) return;
var colliders = colliderBatch.meshLookup.src.data;
Bounds bounds = gameObject.GetComponent<Collider>().bounds;
foreach (var instance in colliders) {
if (instance.collider && instance.collider.bounds.Intersects(bounds)) OnColliderExitZone(instance.collider);
}
}
}
private void OnColliderExitZone(Collider col) {
DecayEntity decayEntity = col.GetComponentInParent<DecayEntity>();
if (decayEntity != null && decayEntity.decay == null) decayEntity.decay = PrefabAttribute.server.Find<Decay>(decayEntity.prefabID);
ResourceDispenser resourceDispenser = col.GetComponentInParent<ResourceDispenser>();
if (resourceDispenser != null) {
instance.OnResourceExitZone(this, resourceDispenser);
return;
}
BaseEntity entity = col.GetComponentInParent<BaseEntity>();
if (entity == null) return;
BaseNpc npc = entity as BaseNpc;
if (npc != null) {
instance.OnNpcExitZone(this, npc);
return;
}
BaseCombatEntity combatEntity = entity as BaseCombatEntity;
if (combatEntity != null && !(entity is LootContainer) && !(entity is BaseHelicopter)) {
buildings.Remove(combatEntity);
instance.OnBuildingExitZone(this, combatEntity);
return;
}
instance.OnOtherExitZone(this, entity);
}
private void OnPlayerEnter(BasePlayer player) {
if (player == null) return;
if (players.Add(player)) {
instance.OnPlayerEnterZone(this, player);
if (definition.Size != Vector3.zero) {
foreach (TriggerBase trigger in triggers) {
if (trigger != null) {
if (trigger.entityContents == null) trigger.entityContents = new HashSet<BaseEntity>();
if (!trigger.entityContents.Contains(player)) player.EnterTrigger(trigger);
}
}
}
}
}
private void OnPlayerExit(BasePlayer player) {
if (player == null) return;
if (players.Remove(player)) {
instance.OnPlayerExitZone(this, player);
if (definition.Size != Vector3.zero) {
foreach (TriggerBase trigger in triggers) {
if (trigger != null) {
if (trigger.entityContents == null) return;
if (!trigger.entityContents.Contains(player)) player.EnterTrigger(trigger);
}
}
}
}
}
#endregion
#region Zone Definition
public class Definition {
public string Name;
public float Radius;
public float Radiation;
public float Comfort;
public float Temperature;
public Vector3 Location;
public Vector3 Size;
public Vector3 Rotation;
public string Id;
public string EnterMessage;
public string LeaveMessage;
public string Permission;
public string EjectSpawns;
public bool Enabled = true;
public ZoneFlags Flags;
public Definition() {
}
public Definition(Vector3 position) {
Radius = 20f;
Location = position;
}
}
#endregion
}
#endregion
private void OnZoneDestroy(Zone zone) {
HashSet<Zone> zones;
foreach (var key in playerZones.Keys.ToArray()) if (playerZones.TryGetValue(key, out zones) && zones.Contains(zone)) OnPlayerExitZone(zone, key);
foreach (var key in buildingZones.Keys.ToArray()) if (buildingZones.TryGetValue(key, out zones) && zones.Contains(zone)) OnBuildingExitZone(zone, key);
foreach (var key in npcZones.Keys.ToArray()) if (npcZones.TryGetValue(key, out zones) && zones.Contains(zone)) OnNpcExitZone(zone, key);
foreach (var key in resourceZones.Keys.ToArray()) if (resourceZones.TryGetValue(key, out zones) && zones.Contains(zone)) OnResourceExitZone(zone, key);
foreach (var key in otherZones.Keys.ToArray()) {
if (!otherZones.TryGetValue(key, out zones)) {
Puts("Zone: {0} Entity: {1} ({2}) {3}", zone.definition.Id, key.GetType(), key.net?.ID, key.IsDestroyed);
continue;
}
if (zones.Contains(zone)) OnOtherExitZone(zone, key);
}
}
#endregion
#region Flags
[Flags]
public enum ZoneFlags : ulong {
None = 0L, AutoLights = 1UL, Eject = 1UL << 1, PvpGod = 1UL << 2, PveGod = 1UL << 3, SleepGod = 1UL << 4, UnDestr = 1UL << 5, NoBuild = 1UL << 6, NoTp = 1UL << 7, NoChat = 1UL << 8, NoGather = 1UL << 9, NoPve = 1UL << 10, NoWounded = 1UL << 11, NoDecay = 1UL << 12, NoDeploy = 1UL << 13, NoKits = 1UL << 14, NoBoxLoot = 1UL << 15, NoPlayerLoot = 1UL << 16, NoCorpse = 1UL << 17, NoSuicide = 1UL << 18, NoRemove = 1UL << 19, NoBleed = 1UL << 20, KillSleepers = 1UL << 21, NpcFreeze = 1UL << 22, NoDrown = 1UL << 23, NoStability = 1UL << 24, NoUpgrade = 1UL << 25, EjectSleepers = 1UL << 26, NoPickup = 1UL << 27, NoCollect = 1UL << 28, NoDrop = 1UL << 29, Kill = 1UL << 30, NoCup = 1UL << 31, AlwaysLights = 1UL << 32, NoTrade = 1UL << 33, NoShop = 1UL << 34, NoSignUpdates = 1UL << 35, NoOvenToggle = 1UL << 36, NoLootSpawns = 1UL << 37, NoNPCSpawns = 1UL << 38, NoVending = 1UL << 39, NoStash = 1UL << 40, NoCraft = 1UL << 41, NoHeliTargeting = 1UL << 42, NoTurretTargeting = 1UL << 43, NoAPCTargeting = 1UL << 44, NoNPCTargeting = 1UL << 45, NoEntityPickup = 1UL << 46, NoMount = 1UL << 47,
}
private bool HasZoneFlag(Zone zone, ZoneFlags flag) => ((disabledFlags & flag) == flag) ? false : (zone.definition.Flags & ~zone.disabledFlags & flag) == flag;
private static bool HasAnyFlag(ZoneFlags flags, ZoneFlags flag) => (flags & flag) != ZoneFlags.None;
private static bool HasAnyZoneFlag(Zone zone) => (zone.definition.Flags & ~zone.disabledFlags) != ZoneFlags.None;
private static void AddZoneFlag(Zone.Definition zone, ZoneFlags flag) => zone.Flags |= flag;
private static void RemoveZoneFlag(Zone.Definition zone, ZoneFlags flag) => zone.Flags &= ~flag;
#endregion
#region External Plugin Hooks
private object canRedeemKit(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoKits, false) ? "You may not redeem a kit inside this area" : null;
private object CanTeleport(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoTp, false) ? "You may not teleport in this area" : null;
private object canRemove(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoRemove, false) ? "You may not use the remover tool in this area" : null;
private bool CanChat(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoChat, false) ? false : true;
private object CanTrade(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoTrade, false) ? "You may not trade in this area" : null;
private object canShop(BasePlayer player) => HasPlayerFlag(player, ZoneFlags.NoShop, false) ? "You may not use the store in this area" : null;
#endregion
#region Zone Edit
// ... [Code preview truncated due to length. Full source in attached file] ...