Heres what i added to allow it to work:
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Text;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Oxide.Plugins
{
[Info("Zone Manager", "k1lly0u", "3.1.10")]
[Description("An advanced management system for creating in-game zones")]
public class ZoneManager : RustPlugin
{
#region Fields
[PluginReference] Plugin Backpacks, PopupNotifications, Spawns;
private StoredData storedData;
private DynamicConfigFile data;
private readonly Hash<string, Zone> zones = new Hash<string, Zone>();
private readonly Hash<Plugin, HashSet<string>> temporaryZones = new Hash<Plugin, HashSet<string>>();
private static readonly Hash<ulong, EntityZones> zonedPlayers = new Hash<ulong, EntityZones>();
private static readonly Hash<NetworkableId, EntityZones> zonedEntities = new Hash<NetworkableId, EntityZones>();
private readonly Dictionary<ulong, string> lastPlayerZone = new Dictionary<ulong, string>();
private readonly ZoneFlags globalFlags = new ZoneFlags();
private readonly ZoneFlags adminBypass = new ZoneFlags();
private static readonly ZoneFlags tempFlags = new ZoneFlags();
private static readonly StringBuilder sb = new StringBuilder();
private bool zonesInitialized = false;
private static ZoneManager Instance { get; set; }
private const string PERMISSION_ZONE = "zonemanager.zone";
private const string PERMISSION_IGNORE_FLAG = "zonemanager.ignoreflag.";
private const int PLAYER_MASK = 131072;
private const int TARGET_LAYERS = ~(1 << 10 | 1 << 18 | 1 << 28 | 1 << 29);
#endregion
#region Oxide Hooks
private void Init()
{
Instance = this;
adminBypass.SetFlags(ZoneFlags.NoBuild, ZoneFlags.NoDeploy, ZoneFlags.NoCup, ZoneFlags.NoUpgrade, ZoneFlags.NoChat, ZoneFlags.NoVoice, ZoneFlags.KillSleepers, ZoneFlags.EjectSleepers, ZoneFlags.NoSignUpdates);
lang.RegisterMessages(Messages, this);
permission.RegisterPermission(PERMISSION_ZONE, this);
foreach (string flag in ZoneFlags.NameToIndex.Keys)
permission.RegisterPermission(PERMISSION_IGNORE_FLAG + flag.ToLower(), this);
LoadData();
}
private void OnServerInitialized() => InitializeZones();
private void OnTerrainInitialized() => InitializeZones();
private void OnPlayerConnected(BasePlayer player) => updateBehaviour.QueueUpdate(player);
private void OnPluginUnloaded(Plugin plugin)
{
if (!temporaryZones.TryGetValue(plugin, out HashSet<string> set))
return;
foreach (string zoneId in set)
{
if (!zones.TryGetValue(zoneId, out Zone zone))
continue;
if (zone.definition.Owner != plugin)
continue;
zones.Remove(zoneId);
UnityEngine.Object.DestroyImmediate(zone.gameObject);
Interface.CallHook("OnZoneErased", zoneId);
}
temporaryZones.Remove(plugin);
}
private void OnEntityKill(BaseEntity baseEntity)
{
if (!baseEntity || !baseEntity.IsValid() || baseEntity.IsDestroyed)
return;
if (!zonedEntities.TryGetValue(baseEntity.net.ID, out EntityZones entityZones))
return;
for (int i = entityZones.Zones.Count - 1; i >= 0; i--)
{
Zone zone = entityZones.Zones[i];
if (!zone)
continue;
zone.OnEntityExitZone(baseEntity, false, true);
}
zonedEntities.Remove(baseEntity.net.ID);
}
private void Unload()
{
DestroyUpdateBehaviour();
foreach (BasePlayer player in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(player, ZMUI);
foreach (KeyValuePair<string, Zone> kvp in zones)
UnityEngine.Object.DestroyImmediate(kvp.Value.gameObject);
zones.Clear();
temporaryZones.Clear();
zonedPlayers.Clear();
zonedEntities.Clear();
Instance = null;
Configuration = null;
}
#endregion
#region UpdateQueue
private UpdateBehaviour m_UpdateBehaviour;
private UpdateBehaviour updateBehaviour
{
get
{
if (m_UpdateBehaviour)
return m_UpdateBehaviour;
m_UpdateBehaviour = new GameObject("ZoneManager.UpdateBehaviour").AddComponent<UpdateBehaviour>();
foreach (BasePlayer player in BasePlayer.activePlayerList)
m_UpdateBehaviour.QueueUpdate(player);
return m_UpdateBehaviour;
}
}
private void DestroyUpdateBehaviour()
{
if (updateBehaviour)
UnityEngine.Object.Destroy(updateBehaviour.gameObject);
}
// Queue and check players for new zones and that they are still in old zones. Previously any plugin that put a player to sleep and teleports them out of a zone
// without calling the OnPlayerSleep hook would bypass a player zone update which would result in players being registered in zones they were no longer in.
// Options are to either continually check and update players, or have every plugin that teleports players call the hook...
private class UpdateBehaviour : MonoBehaviour
{
private readonly System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
private readonly Queue<BasePlayer> playerUpdateQueue = new Queue<BasePlayer>();
private const float MAX_MS = 0.25f;
private void OnDestroy()
{
playerUpdateQueue.Clear();
}
public void QueueUpdate(BasePlayer player)
{
if (!playerUpdateQueue.Contains(player))
playerUpdateQueue.Enqueue(player);
}
public void Reset() => playerUpdateQueue.Clear();
private void Update()
{
if (Time.frameCount % 10 != 0)
return;
sw.Reset();
sw.Start();
while (playerUpdateQueue.Count > 0)
{
if (sw.Elapsed.TotalMilliseconds >= MAX_MS)
{
sw.Stop();
return;
}
BasePlayer player = playerUpdateQueue.Dequeue();
if (!player || !player.IsConnected)
continue;
Instance?.UpdatePlayerZones(player);
InvokeHandler.Invoke(this, () => QueueUpdate(player), 2f);
}
}
}
#endregion
#region Flag Hooks
private void OnEntityBuilt(Planner planner, GameObject gameObject)
{
if (!planner || !gameObject)
return;
BasePlayer player = planner.GetOwnerPlayer();
if (!player)
return;
BaseEntity entity = gameObject.ToBaseEntity();
if (!entity)
return;
if (entity is BuildingBlock block)
{
if (!HasPlayerFlag(player, ZoneFlags.NoBuild))
return;
List<ItemAmount> list = block.BuildCost();
block.Invoke(() =>
{
for (int i = 0; i < list?.Count; i++)
{
ItemAmount itemAmount = list[i];
player.GiveItem(ItemManager.Create(itemAmount.itemDef, Mathf.Clamp(Mathf.RoundToInt(itemAmount.amount), 1, int.MaxValue)));
}
if (entity && !entity.IsDestroyed)
entity.Kill(BaseNetworkable.DestroyMode.Gib);
}, 0.1f);
SendMessage(player, Message("noBuild", player.UserIDString));
}
else if (entity is SimpleBuildingBlock)
{
if (!HasPlayerFlag(player, ZoneFlags.NoBuild))
return;
KillEntityAndReturnItem(player, entity, planner.GetItem());
SendMessage(player, Message("noBuild", player.UserIDString));
}
else
{
if (entity is BuildingPrivlidge)
{
if (!HasPlayerFlag(player, ZoneFlags.NoCup))
return;
KillEntityAndReturnItem(player, entity, planner.GetItem());
SendMessage(player, Message("noCup", player.UserIDString));
}
else
{
if (!HasPlayerFlag(player, ZoneFlags.NoDeploy))
return;
KillEntityAndReturnItem(player, entity, planner.GetItem());
SendMessage(player, Message("noDeploy", player.UserIDString));
}
}
}
private object OnStructureUpgrade(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum grade)
{
if (HasPlayerFlag(player, ZoneFlags.NoUpgrade))
{
SendMessage(player, Message("noUpgrade", player.UserIDString));
return true;
}
return null;
}
private void OnItemDeployed(Deployer deployer, ItemModDeployable itemModDeployable, BaseEntity deployedEntity) // DoDeploy_Regular
{
BasePlayer player = deployer.GetOwnerPlayer();
if (!player)
return;
if (HasPlayerFlag(player, ZoneFlags.NoDeploy))
{
KillEntityAndReturnItem(player, deployedEntity, deployer.GetItem());
SendMessage(player, Message("noDeploy", player.UserIDString));
}
}
private void OnItemDeployed(Deployer deployer, BaseEntity parentEntity, BaseEntity deployedEntity) // DoDeploy_Slot
{
BasePlayer player = deployer.GetOwnerPlayer();
if (!player)
return;
if (HasPlayerFlag(player, ZoneFlags.NoDeploy))
{
KillEntityAndReturnItem(player, deployedEntity, deployer.GetItem());
SendMessage(player, Message("noDeploy", player.UserIDString));
}
}
private void KillEntityAndReturnItem(BasePlayer player, BaseEntity entity, Item item)
{
ItemDefinition itemDefinition = item?.info;
//int amount = item.amount;
ulong skin = item?.skin ?? 0UL;
entity.Invoke(() =>
{
if (entity && !entity.IsDestroyed)
entity.Kill(BaseNetworkable.DestroyMode.Gib);
if (itemDefinition)
player.GiveItem(ItemManager.Create(itemDefinition, 1, skin));
}, 0.1f);
}
private void OnItemUse(Item item, int amount)
{
BaseEntity entity = item?.parent?.entityOwner;
if (!entity)
return;
if (entity is FlameTurret or AutoTurret or GunTrap)
{
if (HasEntityFlag(entity, ZoneFlags.InfiniteTrapAmmo))
item.amount += amount;
return;
}
if (entity is not SearchLight)
return;
if (HasEntityFlag(entity, ZoneFlags.AlwaysLights))
{
item.amount += amount;
return;
}
if (!HasEntityFlag(entity, ZoneFlags.AutoLights))
return;
if (TOD_Sky.Instance.Cycle.Hour > Configuration.AutoLights.OnTime || TOD_Sky.Instance.Cycle.Hour < Configuration.AutoLights.OffTime)
item.amount += amount;
}
private object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)
{
if (!player)
return null;
if (!HasPlayerFlag(player, ZoneFlags.NoChat))
return null;
SendMessage(player, Message("noChat", player.UserIDString));
return true;
}
private object OnBetterChat(Oxide.Core.Libraries.Covalence.IPlayer iPlayer, string message)
{
BasePlayer player = iPlayer.Object as BasePlayer;
return OnPlayerChat(player, message, ConVar.Chat.ChatChannel.Global);
}
private object OnPlayerVoice(BasePlayer player, Byte[] data)
{
if (HasPlayerFlag(player, ZoneFlags.NoVoice))
{
SendMessage(player, Message("noVoice", player.UserIDString));
return true;
}
return null;
}
private object OnServerCommand(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!player || string.IsNullOrEmpty(arg.cmd?.Name))
return null;
if (arg.cmd.Name == "kill" && HasPlayerFlag(player, ZoneFlags.NoSuicide))
{
SendMessage(player, Message("noSuicide", player.UserIDString));
return true;
}
return null;
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (!player)
return;
if (HasPlayerFlag(player, ZoneFlags.KillSleepers))
{
player.Die();
return;
}
if (HasPlayerFlag(player, ZoneFlags.EjectSleepers))
{
if (!zonedPlayers.TryGetValue(player.userID, out EntityZones entityZones) || entityZones.Count == 0)
return;
foreach (Zone zone in entityZones.Zones)
{
if (!zone)
continue;
if (HasFlag(zone, ZoneFlags.EjectSleepers))
{
EjectPlayer(player, zone);
return;
}
}
}
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo)
{
if (!entity || GetEntityComponent<ResourceDispenser>(entity))
return null;
BasePlayer attacker = hitinfo.InitiatorPlayer;
BasePlayer victim = entity as BasePlayer;
if (victim)
{
if (hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Fall)
{
if (HasPlayerFlag(victim, ZoneFlags.NoFallDamage))
return true;
}
if (victim.IsSleeping() && HasPlayerFlag(victim, ZoneFlags.SleepGod))
return true;
if (attacker)
{
if (IsNpc(victim))
return null;
if (HasPlayerFlag(victim, ZoneFlags.PvpGod))
{
if (attacker == victim && hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Suicide)
{
if (HasPlayerFlag(victim, ZoneFlags.NoSuicide))
return true;
return null;
}
if (IsNpc(attacker) && Configuration.NPCHurtPvpGod)
return null;
return true;
}
if (HasPlayerFlag(attacker, ZoneFlags.PvpGod) && !IsNpc(attacker))
return true;
}
else if (HasPlayerFlag(victim, ZoneFlags.PveGod) && !IsNpc(victim))
return true;
else if (hitinfo.Initiator is FireBall && HasPlayerFlag(victim, ZoneFlags.PvpGod))
return true;
return null;
}
BaseNpc baseNpc = entity as BaseNpc;
if (baseNpc)
{
if (HasEntityFlag(baseNpc, ZoneFlags.NoPve))
{
if (attacker && CanBypass(attacker, ZoneFlags.NoPve))
return null;
return true;
}
return null;
}
if (entity is BuildingBlock or SimpleBuildingBlock)
{
if (HasEntityFlag(entity, ZoneFlags.NoBuildingDamage))
{
if (attacker)
{
if (CanBypass(attacker, ZoneFlags.NoBuildingDamage))
return null;
if (HasPlayerFlag(attacker, ZoneFlags.NoBuildingDamage))
return true;
}
if (hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Decay && Configuration.DecayDamageUndestr)
return null;
return true;
}
}
if (entity is not LootContainer && entity is not PatrolHelicopter)
{
if (HasEntityFlag(entity, ZoneFlags.UnDestr))
{
if (attacker)
{
if (CanBypass(attacker, ZoneFlags.UnDestr))
return null;
if (HasPlayerFlag(attacker, ZoneFlags.UnDestr))
return true;
}
if (hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Decay && Configuration.DecayDamageUndestr)
return null;
return true;
}
}
return null;
}
private void OnEntitySpawned(BaseEntity baseEntity)
=> NextTick(() => CanSpawn(baseEntity));
private void CanSpawn(BaseEntity baseEntity)
{
if (!baseEntity.IsValid() || baseEntity.IsDestroyed)
return;
if (Interface.CallHook("CanSpawnInZone", baseEntity) != null)
return;
if (baseEntity is BaseCorpse corpse)
{
if (HasEntityFlag(corpse, ZoneFlags.NoCorpse) && !CanBypass(corpse.OwnerID, ZoneFlags.NoCorpse))
corpse.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
if (baseEntity is LootContainer or JunkPile)
{
if (HasEntityFlag(baseEntity, ZoneFlags.NoLootSpawns))
baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
else if (baseEntity is BaseNpc or NPCPlayer)
{
if (HasEntityFlag(baseEntity, ZoneFlags.NoNPCSpawns))
baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
else if (baseEntity is DroppedItem or WorldItem)
{
if (HasEntityFlag(baseEntity, ZoneFlags.NoDrop))
{
((WorldItem)baseEntity).item.Remove(0f);
baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
}
else if (baseEntity is DroppedItemContainer)
{
if (HasEntityFlag(baseEntity, ZoneFlags.NoDrop))
baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
}
private object CanBeWounded(BasePlayer player, HitInfo hitinfo) => HasPlayerFlag(player, ZoneFlags.NoWounded) ? (object)false : null;
private object CanUpdateSign(BasePlayer player, Signage sign)
{
if (HasPlayerFlag(player, ZoneFlags.NoSignUpdates))
{
SendMessage(player, Message("noSignUpdates", player.UserIDString));
return false;
}
return null;
}
private object OnOvenToggle(BaseOven oven, BasePlayer player)
{
if (HasPlayerFlag(player, ZoneFlags.NoOvenToggle))
{
SendMessage(player, Message("noOvenToggle", player.UserIDString));
return true;
}
return null;
}
private object CanUseVending(BasePlayer player, VendingMachine machine)
{
if (HasPlayerFlag(player, ZoneFlags.NoVending))
{
SendMessage(player, Message("noVending", player.UserIDString));
return false;
}
return null;
}
private object CanHideStash(BasePlayer player, StashContainer stash)
{
if (HasPlayerFlag(player, ZoneFlags.NoStash))
{
SendMessage(player, Message("noStash", player.UserIDString));
return false;
}
return null;
}
private object CanCraft(ItemCrafter itemCrafter, ItemBlueprint bp, int amount)
{
BasePlayer player = itemCrafter.GetComponent<BasePlayer>();
if (player && HasPlayerFlag(player, ZoneFlags.NoCraft))
{
SendMessage(player, Message("noCraft", player.UserIDString));
return false;
}
return null;
}
private void OnDoorOpened(Door door, BasePlayer player)
{
if (HasPlayerFlag(player, ZoneFlags.NoDoorAccess))
{
SendMessage(player, Message("noDoor", player.UserIDString));
door.CloseRequest();
}
}
private object OnSprayCreate(SprayCan sprayCan, Vector3 position, Quaternion rotation)
{
if (!sprayCan)
return null;
BasePlayer player = sprayCan.GetOwnerPlayer();
if (!player)
return null;
if (HasPlayerFlag(player, ZoneFlags.NoSprays))
{
SendMessage(player, Message("nosprays", 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) || (target != null && HasPlayerFlag(target, ZoneFlags.NoPlayerLoot)))
{
if (looter == target && Backpacks != null)
{
object hookResult = Backpacks.Call("CanLootPlayer", target, looter);
if (hookResult is bool result && result)
return true;
}
SendMessage(looter, Message("noLoot", looter.UserIDString));
NextTick(looter.EndLooting);
return false;
}
return null;
}
private void OnLootEntity(BasePlayer player, BaseEntity entity)
{
if (entity is LootableCorpse corpse)
OnLootCorpse(corpse, player);
if (entity is DroppedItemContainer container)
OnLootContainer(container, player);
if (entity is StorageContainer)
OnLootInternal(player, ZoneFlags.NoBoxLoot);
}
private object CanLootEntity(BasePlayer player, LootableCorpse corpse)
{
if (corpse is NPCPlayerCorpse)
{
if (!HasPlayerFlag(player, ZoneFlags.NoNPCLoot))
return null;
SendMessage(player, Message("noLoot", player.UserIDString));
return false;
}
if (corpse.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf))
return null;
return CanLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private void OnLootCorpse(LootableCorpse corpse, BasePlayer player)
{
if (corpse is NPCPlayerCorpse)
{
if (!HasPlayerFlag(player, ZoneFlags.NoNPCLoot))
return;
SendMessage(player, Message("noLoot", player.UserIDString));
NextTick(player.EndLooting);
return;
}
if (corpse.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf))
return;
OnLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private void OnLootContainer(DroppedItemContainer container, BasePlayer player)
{
if (container.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf))
return;
OnLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private object CanLootEntity(BasePlayer player, DroppedItemContainer container)
{
if (container.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf))
return null;
return CanLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private object CanLootEntity(BasePlayer player, StorageContainer container) => CanLootInternal(player, ZoneFlags.NoBoxLoot);
private object CanLootInternal(BasePlayer player, int flag)
{
if (!player || !HasPlayerFlag(player, flag))
return null;
SendMessage(player, Message("noLoot", player.UserIDString));
return false;
}
private void OnLootInternal(BasePlayer player, int flag)
{
if (!player || !HasPlayerFlag(player, flag))
return;
SendMessage(player, Message("noLoot", player.UserIDString));
NextTick(player.EndLooting);
}
#endregion
#region Pickup Hooks
private object CanPickupEntity(BasePlayer player, BaseCombatEntity entity) => 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, int flag)
{
if (!HasPlayerFlag(player, flag))
return null;
SendMessage(player, Message("noPickup", player.UserIDString));
return false;
}
#endregion
#region Gather Hooks
private object CanLootEntity(ResourceContainer container, BasePlayer player) => OnGatherInternal(player);
private object OnCollectiblePickup(Item item, BasePlayer player) => OnGatherInternal(player);
private object OnGrowableGather(GrowableEntity plant, Item item, BasePlayer player) => OnGatherInternal(player);
private object OnDispenserGather(ResourceDispenser dispenser, BasePlayer player, Item item) => OnGatherInternal(player);
private object OnGatherInternal(BasePlayer player)
{
if (!player || !HasPlayerFlag(player, ZoneFlags.NoGather))
return null;
SendMessage(player, Message("noGather", player.UserIDString));
return true;
}
#endregion
#region Targeting Hooks
private object OnTurretTarget(AutoTurret turret, BasePlayer player) => OnTargetPlayerInternal(player, ZoneFlags.NoTurretTargeting);
private object CanBradleyApcTarget(BradleyAPC apc, BasePlayer player)
{
if (player && HasPlayerFlag(player, ZoneFlags.NoAPCTargeting))
return false;
return null;
}
private object CanHelicopterTarget(PatrolHelicopterAI heli, BasePlayer player)
{
if (!player || !HasPlayerFlag(player, ZoneFlags.NoHeliTargeting))
return null;
heli.interestZoneOrigin = heli.GetRandomPatrolDestination();
return false;
}
private object CanHelicopterStrafeTarget(PatrolHelicopterAI heli, BasePlayer player)
{
if (player && HasPlayerFlag(player, ZoneFlags.NoHeliTargeting))
return false;
return null;
}
private object OnHelicopterTarget(HelicopterTurret turret, BasePlayer player) => OnTargetPlayerInternal(player, ZoneFlags.NoHeliTargeting);
private object OnNpcTarget(BaseCombatEntity entity, BasePlayer player) => OnTargetPlayerInternal(player, ZoneFlags.NoNPCTargeting);
private object OnTargetPlayerInternal(BasePlayer player, int flag)
{
if (player && HasPlayerFlag(player, flag))
return true;
return null;
}
#endregion
#region Mounting Hooks
private object CanMountEntity(BasePlayer player, BaseMountable entity)
{
if (!player || !entity)
return null;
if (!entity.VehicleParent())
return null;
if (!HasPlayerFlag(player, ZoneFlags.NoVehicleMounting))
return null;
SendMessage(player, Message("novehiclemounting", player.UserIDString));
return false;
}
private object CanDismountEntity(BasePlayer player, BaseMountable entity)
{
if (!player || !entity)
return null;
if (!entity.VehicleParent())
return null;
if (!HasPlayerFlag(player, ZoneFlags.NoVehicleDismounting))
return null;
SendMessage(player, Message("novehicledismounting", player.UserIDString));
return false;
}
#endregion
#region Additional KillSleeper Checks
private void OnPlayerSleep(BasePlayer player)
{
if (!player)
return;
//player.Invoke(()=> UpdatePlayerZones(player), 1f); // Manually update the zones a player is in. Sleeping players don't trigger OnTriggerEnter or OnTriggerExit
timer.In(2f, () =>
{
if (!player || !player.IsSleeping())
return;
if (player.IsConnected)
return;
if (HasPlayerFlag(player, ZoneFlags.KillSleepers))
{
player.Invoke(() => KillSleepingPlayer(player), 3f);
return;
}
if (HasPlayerFlag(player, ZoneFlags.EjectSleepers))
{
player.Invoke(() =>
{
if (!player || !player.IsSleeping())
return;
if (!zonedPlayers.TryGetValue(player.userID, out EntityZones entityZones) || entityZones.Count == 0)
return;
foreach (Zone zone in entityZones.Zones)
{
if (!zone)
continue;
if (HasFlag(zone, ZoneFlags.EjectSleepers))
EjectPlayer(player, zone);
}
}, 3f);
}
});
}
private void OnPlayerSleepEnd(BasePlayer player) => updateBehaviour.QueueUpdate(player);
private void KillSleepingPlayer(BasePlayer player)
{
if (!player || !player.IsSleeping())
return;
if (!HasPlayerFlag(player, ZoneFlags.KillSleepers))
return;
if (player.IsConnected)
OnPlayerSleep(player);
else player.Die();
}
private void UpdatePlayerZones(BasePlayer player)
{
if (!player)
return;
if (zonedPlayers.TryGetValue(player.userID, out EntityZones entityZones))
{
List<Zone> list = Pool.Get<List<Zone>>();
list.AddRange(entityZones.Zones);
for (int i = list.Count - 1; i >= 0; i--)
{
Zone zone = list[i];
if (!zone || !zone.definition.Enabled)
continue;
if (zone.definition.Size != Vector3.zero)
{
if (!IsInsideBounds(zone, player.transform.position))
OnPlayerExitZone(player, zone);
}
else
{
if (Vector3.Distance(player.transform.position, zone.transform.position) > zone.definition.Radius)
OnPlayerExitZone(player, zone);
}
}
Pool.FreeUnmanaged(ref list);
}
foreach (Zone zone in zones.Values)
{
if (!zone)
continue;
if (entityZones != null && entityZones.Zones.Contains(zone))
continue;
if (zone.definition.Size != Vector3.zero)
{
if (IsInsideBounds(zone, player.transform.position))
OnPlayerEnterZone(player, zone);
}
else
{
if (Vector3.Distance(player.transform.position, zone.transform.position) <= zone.definition.Radius)
OnPlayerEnterZone(player, zone);
}
}
if (player.HasPlayerFlag(BasePlayer.PlayerFlags.SafeZone) && !player.InSafeZone())
player.SetPlayerFlag(BasePlayer.PlayerFlags.SafeZone, false);
}
private bool IsInsideBounds(Zone zone, Vector3 worldPos) => zone?.collider?.ClosestPoint(worldPos) == worldPos;
#endregion
private T GetEntityComponent<T>(BaseEntity entity) where T : EntityComponentBase
{
for (int i = 0; i < entity.Components.Count; i++)
{
EntityComponentBase component = entity.Components[i];
if (component is T t)
return t;
}
return null;
}
#endregion
#region Zone Functions
private void InitializeZones()
{
if (zonesInitialized)
return;
foreach (Zone.Definition definition in storedData.definitions)
{
Zone zone = new GameObject().AddComponent<Zone>();
zone.InitializeZone(definition);
zones.Add(definition.Id, zone);
}
foreach (Zone zone in zones.Values)
zone.FindZoneParent();
zonesInitialized = true;
UnsubscribeAll();
UpdateHookSubscriptions();
}
private static bool ReverseVelocity(BaseVehicle baseVehicle)
{
if (baseVehicle is BaseVehicleModule module)
baseVehicle = module.Vehicle;
if (!baseVehicle || !baseVehicle.IsVehicleRoot() || !baseVehicle.rigidBody)
return false;
if (baseVehicle.AnyMounted() && baseVehicle is BaseHelicopter)
{
baseVehicle.rigidBody.velocity *= -1f;
Vector3 euler = baseVehicle.transform.eulerAngles;
baseVehicle.transform.rotation = Quaternion.Euler(euler.x, euler.y - 180f, euler.z);
}
else
{
Vector3 force = (baseVehicle.rigidBody.velocity.normalized * -1f) * baseVehicle.rigidBody.mass * 4f;
baseVehicle.rigidBody.velocity = Vector3.zero;
baseVehicle.rigidBody.AddForce(force, ForceMode.Impulse);
}
return true;
}
private void EjectPlayer(BasePlayer player, Zone zone)
{
if (zone.keepInList.Contains(player.userID.Get()) || zone.whitelist.Contains(player.userID.Get()))
return;
if (!string.IsNullOrEmpty(zone.definition.Permission))
{
if (HasPermission(player, zone.definition.Permission))
return;
}
if (player.isMounted && ReverseVelocity(player.GetMountedVehicle()))
{
SendMessage(player, Message("eject", player.UserIDString));
return;
}
Vector3 position = Vector3.zero;
if (Spawns && !string.IsNullOrEmpty(zone.definition.EjectSpawns))
{
object success = Spawns.Call("GetRandomSpawn", zone.definition.EjectSpawns);
if (success is Vector3 vector3)
position = vector3;
}
if (position == Vector3.zero)
{
float distance = zone.definition.Size != Vector3.zero ? Mathf.Max(zone.definition.Size.x, zone.definition.Size.z) : zone.definition.Radius;
position = zone.transform.position + (((player.transform.position.XZ3D() - zone.transform.position.XZ3D()).normalized) * (distance + 10f));
if (Physics.Raycast(new Ray(new Vector3(position.x, position.y + 300, position.z), Vector3.down), out RaycastHit rayHit, 500, TARGET_LAYERS, QueryTriggerInteraction.Ignore))
position.y = rayHit.point.y;
else position.y = TerrainMeta.HeightMap.GetHeight(position);
}
player.MovePosition(position);
player.ClientRPCPlayer(null, player, "ForcePositionTo", player.transform.position);
player.SendNetworkUpdateImmediate();
SendMessage(player, Message("eject", player.UserIDString));
}
private void AttractPlayer(BasePlayer player, Zone zone)
{
if (player.isMounted && ReverseVelocity(player.GetMountedVehicle()))
{
SendMessage(player, Message("attract", player.UserIDString));
return;
}
float distance = zone.definition.Size != Vector3.zero ? Mathf.Max(zone.definition.Size.x, zone.definition.Size.z) : zone.definition.Radius;
Vector3 position = zone.transform.position + (player.transform.position - zone.transform.position).normalized * (distance - 5f);
position.y = TerrainMeta.HeightMap.GetHeight(position);
player.MovePosition(position);
player.ClientRPCPlayer(null, player, "ForcePositionTo", player.transform.position);
player.SendNetworkUpdateImmediate();
SendMessage(player, Message("attract", player.UserIDString));
}
private void ShowZone(BasePlayer player, string zoneId, float time = 30)
{
Zone zone = GetZoneByID(zoneId);
if (!zone)
return;
if (zone.definition.Size != Vector3.zero)
{
Vector3 center = zone.definition.Location;
Quaternion rotation = Quaternion.Euler(zone.definition.Rotation);
Vector3 size = zone.definition.Size / 2;
Vector3 point1 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z + size.z), center, rotation);
Vector3 point2 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z + size.z), center, rotation);
Vector3 point3 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y + size.y, center.z - size.z), center, rotation);
Vector3 point4 = RotatePointAroundPivot(new Vector3(center.x + size.x, center.y - size.y, center.z - size.z), center, rotation);
Vector3 point5 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z + size.z), center, rotation);
Vector3 point6 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z + size.z), center, rotation);
Vector3 point7 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y + size.y, center.z - size.z), center, rotation);
Vector3 point8 = RotatePointAroundPivot(new Vector3(center.x - size.x, center.y - size.y, center.z - size.z), center, rotation);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point1, point2);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point1, point3);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point1, point5);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point4, point2);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point4, point3);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point4, point8);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point5, point6);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point5, point7);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point6, point2);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point8, point6);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point8, point7);
player.SendConsoleCommand("ddraw.line", time, Color.blue, point7, point3);
}
else player.SendConsoleCommand("ddraw.sphere", time, Color.blue, zone.definition.Location, zone.definition.Radius);
}
private Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation) => rotation * (point - pivot) + pivot;
#endregion
#region Component
public class Zone : MonoBehaviour
{
public Definition definition;
public ZoneFlags disabledFlags = new ZoneFlags();
public Zone parent;
public List<BasePlayer> players = Pool.Get<List<BasePlayer>>();
public List<BaseEntity> entities = Pool.Get<List<BaseEntity>>();
private List<IOEntity> ioEntities = Pool.Get<List<IOEntity>>();
public List<ulong> keepInList = Pool.Get<List<ulong>>();
public List<ulong> whitelist = Pool.Get<List<ulong>>();
public Hash<ulong, EntityZones> entityZones = new Hash<ulong, EntityZones>();
private Rigidbody rigidbody;
public Collider collider;
public Bounds colliderBounds;
private ChildSphereTrigger<TriggerRadiation> radiation;
private ChildSphereTrigger<TriggerComfort> comfort;
private ChildSphereTrigger<TriggerTemperature> temperature;
private ChildSphereTrigger<TriggerSafeZone> safeZone;
private readonly Hash<BaseVehicle, float> lastReversedTimes = new Hash<BaseVehicle, float>();
private int creationFrame;
private bool isTogglingLights = false;
private void Awake()
{
gameObject.layer = (int)Layer.Reserved1;
gameObject.name = "ZoneManager";
enabled = false;
creationFrame = Time.frameCount;
}
private void OnDestroy()
{
EmptyZone();
Pool.FreeUnmanaged(ref players);
Pool.FreeUnmanaged(ref entities);
Pool.FreeUnmanaged(ref ioEntities);
Pool.FreeUnmanaged(ref keepInList);
Pool.FreeUnmanaged(ref whitelist);
Interface.CallHook("OnZoneDestroyed", definition.Id);
}
private void EmptyZone()
{
RemovePlayersFromTriggers();
keepInList.Clear();
ioEntities.Clear();
for (int i = players.Count - 1; i >= 0; i--)
Instance?.OnPlayerExitZone(players[i], this);
for (int i = entities.Count - 1; i >= 0; i--)
Instance?.OnEntityExitZone(entities[i], this);
}
#region Zone Initialization
public void InitializeZone(Definition definition)
{
if (this.definition == null)
Interface.CallHook("OnZoneInitialize", definition.Id);
this.definition = definition;
transform.position = definition.Location;
transform.rotation = Quaternion.Euler(definition.Rotation);
if (definition.Enabled)
{
RegisterPermission();
InitializeCollider();
InitializeAutoLights();
InitializeRadiation();
InitializeSafeZone();
InitializeComfort();
InitializeTemperature();
RemovePlayersFromTriggers();
AddPlayersToTriggers();
OnZoneFlagsChanged();
}
else
{
InvokeHandler.CancelInvoke(this, CheckAlwaysLights);
InvokeHandler.CancelInvoke(this, CheckLights);
if (isLightsOn)
ServerMgr.Instance.StartCoroutine(ToggleLights(false));
EmptyZone();
if (collider)
DestroyImmediate(collider);
if (rigidbody)
DestroyImmediate(rigidbody);
}
enabled = definition.Enabled;
}
public void FindZoneParent()
{
if (string.IsNullOrEmpty(definition.ParentID))
return;
if (Instance == null)
{
Debug.LogError($"[ZoneManager] Zone attempted to find parent zone, but plugin instance is null...");
return;
}
Instance.zones.TryGetValue(definition.ParentID, out parent);
}
public void Reset()
{
InvokeHandler.CancelInvoke(this, CheckAlwaysLights);
InvokeHandler.CancelInvoke(this, CheckLights);
if (isLightsOn)
ServerMgr.Instance.StartCoroutine(ToggleLights(false));
EmptyZone();
InitializeZone(definition);
FindZoneParent();
}
public void OnZoneFlagsChanged()
{
if (HasFlag(ZoneFlags.PoweredSwitches))
{
if (!InvokeHandler.IsInvoking(this, IOTick))
InvokeHandler.InvokeRandomized(this, IOTick, 0f, 1f, 0.1f);
}
else InvokeHandler.CancelInvoke(this, IOTick);
}
private void RegisterPermission()
{
if (Instance == null)
{
Debug.LogError($"[ZoneManager] Zone attempted to register permission, but plugin instance is null...");
return;
}
if (!string.IsNullOrEmpty(definition.Permission) && !Instance.permission.PermissionExists(definition.Permission))
Instance.permission.RegisterPermission(definition.Permission, Instance);
}
private void InitializeCollider()
{
if (collider)
DestroyImmediate(collider);
if (rigidbody)
DestroyImmediate(rigidbody);
rigidbody = gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = false;
rigidbody.isKinematic = true;
rigidbody.detectCollisions = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
SphereCollider sphereCollider = gameObject.GetComponent<SphereCollider>();
BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
if (definition.Size != Vector3.zero)
{
if (sphereCollider)
Destroy(sphereCollider);
if (!boxCollider)
{
boxCollider = gameObject.AddComponent<BoxCollider>();
boxCollider.isTrigger = true;
}
boxCollider.size = definition.Size;
colliderBounds = boxCollider.bounds;
collider = boxCollider;
}
else
{
if (boxCollider)
Destroy(boxCollider);
if (!sphereCollider)
{
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.isTrigger = true;
}
sphereCollider.radius = definition.Radius;
colliderBounds = sphereCollider.bounds;
collider = sphereCollider;
}
}
#endregion
#region Triggers
private void InitializeRadiation()
{
if (definition.Radiation > 0)
{
radiation ??= new ChildSphereTrigger<TriggerRadiation>(gameObject, "Radiation");
radiation.Trigger.RadiationAmountOverride = definition.Radiation;
radiation.Collider.radius = collider is SphereCollider ? definition.Radius : Mathf.Min(definition.Size.x, definition.Size.y, definition.Size.z) * 0.5f;
radiation.Trigger.enabled = this.enabled;
}
else radiation?.Destroy();
}
private void InitializeComfort()
{
if (definition.Comfort > 0)
{
comfort ??= new ChildSphereTrigger<TriggerComfort>(gameObject, "Comfort");
comfort.Trigger.baseComfort = definition.Comfort;
comfort.Trigger.triggerSize = comfort.Collider.radius = collider is SphereCollider ? definition.Radius : Mathf.Min(definition.Size.x, definition.Size.y, definition.Size.z) * 0.5f;
comfort.Trigger.enabled = this.enabled;
}
else comfort?.Destroy();
}
private void InitializeTemperature()
{
if (definition.Temperature != 0)
{
temperature ??= new ChildSphereTrigger<TriggerTemperature>(gameObject, "Temperature");
temperature.Trigger.Temperature = definition.Temperature;
temperature.Trigger.triggerSize = temperature.Collider.radius = collider is SphereCollider ? definition.Radius : Mathf.Min(definition.Size.x, definition.Size.y, definition.Size.z) * 0.5f;
temperature.Trigger.enabled = this.enabled;
}
else temperature?.Destroy();
}
private void InitializeSafeZone()
{
if (definition.SafeZone)
{
safeZone ??= new ChildSphereTrigger<TriggerSafeZone>(gameObject, "SafeZone");
}
else safeZone?.Destroy();
}
private void AddToTrigger(TriggerBase triggerBase, BasePlayer player)
{
if (!triggerBase || !player)
return;
triggerBase.entityContents ??= new HashSet<BaseEntity>();
if (!triggerBase.entityContents.Add(player))
return;
player.EnterTrigger(triggerBase);
if (triggerBase is not TriggerSafeZone)
return;
if (player.IsItemHoldRestricted(player.inventory.containerBelt.FindItemByUID(player.svActiveItemID)))
player.UpdateActiveItem(default(ItemId));
player.SetPlayerFlag(BasePlayer.PlayerFlags.SafeZone, true);
}
private void RemoveFromTrigger(TriggerBase triggerBase, BasePlayer player)
{
if (!triggerBase || !player)
return;
if (triggerBase.entityContents == null || !triggerBase.entityContents.Contains(player))
return;
triggerBase.entityContents.Remove(player);
player.LeaveTrigger(triggerBase);
if (triggerBase is not TriggerSafeZone)
return;
if (!player.InSafeZone())
player.SetPlayerFlag(BasePlayer.PlayerFlags.SafeZone, false);
}
private void AddPlayersToTriggers()
{
for (int i = 0; i < players.Count; i++)
{
BasePlayer player = players[i];
if (safeZone != null)
AddToTrigger(safeZone.Trigger, player);
if (radiation != null)
AddToTrigger(radiation.Trigger, player);
if (comfort != null)
AddToTrigger(comfort.Trigger, player);
if (temperature != null)
AddToTrigger(temperature.Trigger, player);
}
}
private void RemovePlayersFromTriggers()
{
for (int i = 0; i < players.Count; i++)
{
BasePlayer player = players[i];
if (safeZone != null)
RemoveFromTrigger(safeZone.Trigger, player);
if (radiation != null)
RemoveFromTrigger(radiation.Trigger, player);
if (comfort != null)
RemoveFromTrigger(comfort.Trigger, player);
if (temperature != null)
RemoveFromTrigger(temperature.Trigger, player);
}
}
private class ChildSphereTrigger<T> where T : TriggerBase
{
public GameObject Object { get; private set; }
public SphereCollider Collider { get; private set; }
public T Trigger { get; private set; }
public ChildSphereTrigger(GameObject parent, string name)
{
Object = parent.CreateChild();
Object.name = name;
Object.layer = (int)Layer.TransparentFX;
Collider = Object.AddComponent<SphereCollider>();
Collider.isTrigger = true;
Trigger = Object.AddComponent<T>();
Trigger.interestLayers = 0;
}
public void Destroy() => UnityEngine.Object.Destroy(Object);
}
#endregion
#region Autolights
private bool isLightsOn = false;
private void InitializeAutoLights()
{
if (HasFlag(ZoneFlags.AlwaysLights))
{
isLightsOn = true;
InvokeHandler.CancelInvoke(this, CheckAlwaysLights);
InvokeHandler.InvokeRandomized(this, CheckAlwaysLights, 5f, 60f, 10f);
}
else if (HasFlag(ZoneFlags.AutoLights))
{
InvokeHandler.CancelInvoke(this, CheckLights);
InvokeHandler.InvokeRandomized(this, CheckLights, 5f, 20f, 10f);
}
}
private void CheckAlwaysLights()
{
ServerMgr.Instance.StartCoroutine(ToggleLights(true));
}
private void CheckLights()
{
float currentTime = TOD_Sky.Instance.Cycle.Hour;
bool shouldBeActive = currentTime > Configuration.AutoLights.OnTime || currentTime < Configuration.AutoLights.OffTime;
if (shouldBeActive == isLightsOn)
return;
isLightsOn = shouldBeActive;
ServerMgr.Instance.StartCoroutine(ToggleLights(isLightsOn));
}
private IEnumerator ToggleLights(bool active)
{
while (isTogglingLights)
yield return null;
isTogglingLights = true;
bool requiresFuel = Configuration.AutoLights.RequiresFuel;
for (int i = 0; i < entities.Count; i++)
{
if (ToggleLight(entities[i], active, requiresFuel))
yield return CoroutineEx.waitForEndOfFrame;
}
isTogglingLights = false;
}
private bool ToggleLight(BaseEntity baseEntity, bool active, bool requiresFuel)
{
BaseOven baseOven = baseEntity as BaseOven;
if (baseOven)
{
if (active)
{
if (!baseOven.IsOn())
{
if ((requiresFuel && baseOven.FindBurnable() != null) || !requiresFuel)
baseOven.SetFlag(BaseEntity.Flags.On, true);
}
}
else
{
if (baseOven.IsOn())
baseOven.StopCooking();
}
return true;
}
SearchLight searchLight = baseEntity as SearchLight;
if (searchLight)
{
if (active)
{
if (!searchLight.IsOn())
searchLight.SetFlag(BaseEntity.Flags.On, true);
}
else
{
if (searchLight.IsOn())
searchLight.SetFlag(BaseEntity.Flags.On, false);
}
return true;
}
return false;
}
#endregion
#region Entity Detection
private void OnTriggerEnter(Collider col)
{
if (!definition.Enabled || !col || !col.gameObject)
return;
BaseEntity baseEntity = col.gameObject.ToBaseEntity();
if (!baseEntity || !baseEntity.IsValid())
return;
if (baseEntity is BasePlayer { IsNpc: false } player)
{
Instance?.OnPlayerEnterZone(player, this);
if (parent)
Instance?.UpdateZoneEntityFlags(this);
return;
}
Instance?.OnEntityEnterZone(baseEntity, this);
}
private void OnTriggerExit(Collider col)
{
if (!definition.Enabled || !col || !col.gameObject)
return;
BaseEntity baseEntity = col.gameObject.ToBaseEntity();
if (!baseEntity || !baseEntity.IsValid())
return;
if (baseEntity is BasePlayer { IsNpc: false } player)
{
Instance?.OnPlayerExitZone(player, this);
return;
}
Instance?.OnEntityExitZone(baseEntity, this);
}
public void OnPlayerEnterZone(BasePlayer player)
{
if (!players.Contains(player))
players.Add(player);
if (zonedPlayers.TryGetValue(player.userID, out EntityZones entityZone))
entityZones[player.userID] = entityZone;
if (safeZone != null)
AddToTrigger(safeZone.Trigger, player);
if (radiation != null)
AddToTrigger(radiation.Trigger, player);
if (comfort != null)
AddToTrigger(comfort.Trigger, player);
if (temperature != null)
AddToTrigger(temperature.Trigger, player);
}
public void OnPlayerExitZone(BasePlayer player)
{
players.Remove(player);
entityZones.Remove(player.userID);
if (safeZone != null)
RemoveFromTrigger(safeZone.Trigger, player);
if (radiation != null)
RemoveFromTrigger(radiation.Trigger, player);
if (comfort != null)
RemoveFromTrigger(comfort.Trigger, player);
if (temperature != null)
RemoveFromTrigger(temperature.Trigger, player);
}
public void OnEntityEnterZone(BaseEntity baseEntity)
{
entities.Add(baseEntity);
if (zonedEntities.TryGetValue(baseEntity.net.ID, out EntityZones entityZone))
entityZones[baseEntity.net.ID.Value] = entityZone;
if (HasFlag(ZoneFlags.NoDecay))
{
DecayEntity decayEntity = baseEntity.GetComponentInParent<DecayEntity>();
if (decayEntity)
{
decayEntity.decay = null;
}
}
if (HasFlag(ZoneFlags.NoStability))
{
if (baseEntity is StabilityEntity entity)
{
entity.grounded = true;
}
}
if (HasFlag(ZoneFlags.NpcFreeze) && baseEntity.IsNpc)
{
if (baseEntity is BaseAnimalNPC animalNpc)
{
animalNpc.brain.SetEnabled(false);
return;
}
if (baseEntity is global::HumanNPC humanNpc)
{
humanNpc.Brain.SetEnabled(false);
return;
}
if (baseEntity is ScarecrowNPC scarecrowNpc)
{
scarecrowNpc.Brain.SetEnabled(false);
return;
}
if (baseEntity is BaseNpc npc)
{
npc.CancelInvoke(npc.TickAi);
return;
}
}
if (baseEntity is SmartSwitch or ElectricSwitch or RFReceiver)
{
ioEntities.Add((IOEntity)baseEntity);
if (HasFlag(ZoneFlags.PoweredSwitches))
{
((IOEntity)baseEntity).SetFlag(BaseEntity.Flags.Reserved8, true);
((IOEntity)baseEntity).currentEnergy = int.MaxValue;
}
}
if (HasFlag(ZoneFlags.AlwaysLights) || (HasFlag(ZoneFlags.AutoLights) && isLightsOn))
{
if (baseEntity is BaseOven or SearchLight)
{
ToggleLight(baseEntity, true, Configuration.AutoLights.RequiresFuel);
}
}
}
public void OnEntityExitZone(BaseEntity baseEntity, bool resetDecay, bool isDead = false)
{
entities.Remove(baseEntity);
entityZones.Remove(baseEntity.net.ID.Value);
if (isDead)
return;
if (resetDecay)
{
if (HasFlag(ZoneFlags.NoDecay))
{
DecayEntity decayEntity = baseEntity.GetComponentInParent<DecayEntity>();
if (decayEntity)
{
decayEntity.decay = PrefabAttribute.server.Find<Decay>(decayEntity.prefabID);
}
}
}
if (HasFlag(ZoneFlags.NpcFreeze) && baseEntity.IsNpc)
{
if (baseEntity is BaseAnimalNPC animalNpc)
{
animalNpc.brain.SetEnabled(true);
return;
}
if (baseEntity is global::HumanNPC humanNpc)
{
humanNpc.Brain.SetEnabled(true);
return;
}
if (baseEntity is ScarecrowNPC scarecrowNpc)
{
scarecrowNpc.Brain.SetEnabled(true);
return;
}
if (baseEntity is BaseNpc npc)
{
npc.InvokeRandomized(npc.TickAi, 0.1f, 0.1f, 0.00500000035f);
return;
}
}
if (baseEntity is SmartSwitch or ElectricSwitch or RFReceiver)
{
IOEntity ioEntity = (IOEntity)baseEntity;
ioEntities.Remove(ioEntity);
if (HasFlag(ZoneFlags.PoweredSwitches))
{
ioEntity.SetFlag(BaseEntity.Flags.Reserved8, false);
ioEntity.currentEnergy = 0;
for (int i = 0; i < ioEntity.inputs.Length; i++)
{
IOEntity fromEntity = ioEntity.inputs[i].connectedTo.Get();
if (fromEntity)
fromEntity.MarkDirtyForceUpdateOutputs();
}
}
}
if (!HasFlag(ZoneFlags.AlwaysLights) && (!HasFlag(ZoneFlags.AutoLights) || !isLightsOn))
return;
if (baseEntity is BaseOven or SearchLight)
{
ToggleLight(baseEntity, false, false);
}
}
#endregion
#region IO Power
private void IOTick()
{
for (int i = 0; i < ioEntities.Count; i++)
{
IOEntity ioEntity = ioEntities[i];
if (!ioEntity || ioEntity.IsDestroyed)
continue;
ioEntity.SetFlag(BaseEntity.Flags.Reserved8, true);
ioEntity.currentEnergy = int.MaxValue;
}
}
#endregion
#region Vehicle Enter/Exit
public bool TryReverseVelocity(BaseEntity baseEntity)
{
if (Time.frameCount == creationFrame)
return false;
if (baseEntity is not BaseVehicle baseVehicle)
return false;
if (baseVehicle is BaseVehicleModule module)
baseVehicle = module.Vehicle;
if (!CanReverseVelocity(baseVehicle))
return false;
if (!ReverseVelocity(baseVehicle))
return false;
lastReversedTimes[baseVehicle] = Time.time;
return true;
}
private bool CanReverseVelocity(BaseVehicle baseVehicle)
{
if (lastReversedTimes.TryGetValue(baseVehicle, out float lastReversedTime))
return Time.time - lastReversedTime > 0.5f;
return true;
}
#endregion
#region Helpers
public bool HasPermission(BasePlayer player)
{
if (Instance == null)
{
Debug.LogError($"[ZoneManager] Zone attempted to check player permission, but plugin instance is null...");
return false;
}
return string.IsNullOrEmpty(definition.Permission) || Instance.permission.UserHasPermission(player.UserIDString, definition.Permission);
}
public bool CanLeaveZone(BasePlayer player) => !keepInList.Contains(player.userID.Get());
public bool CanEnterZone(BasePlayer player) => HasPermission(player) || !CanLeaveZone(player) || whitelist.Contains(player.userID.Get());
#endregion
#region Flags
public void AddFlag(int flag)
{
definition.Flags.AddFlag(flag);
OnZoneFlagsChanged();
}
public void RemoveFlag(int flag)
{
definition.Flags.RemoveFlag(flag);
OnZoneFlagsChanged();
}
public bool HasFlag(int flag) => definition.Flags.HasFlag(flag) && !disabledFlags.HasFlag(flag);
public bool HasDisabledFlag(int flag) => disabledFlags.HasFlag(flag);
public void AddDisabledFlag(int flag)
{
disabledFlags.AddFlag(flag);
OnZoneFlagsChanged();
}
public void RemoveDisabledFlag(int flag)
{
disabledFlags.AddFlag(flag);
OnZoneFlagsChanged();
}
#endregion
#region Zone Definition
public class Definition
{
public string Id { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Name { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public float Radius { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public float Radiation { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public float Comfort { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public float Temperature { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool SafeZone { get; set; }
public Vector3 Location { get; set; }
public Vector3 Size { get; set; }
public Vector3 Rotation { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string ParentID { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string EnterMessage { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string LeaveMessage { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Permission { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public string EjectSpawns { get; set; }
[DefaultValue(true)]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool Enabled { get; set; } = true;
[JsonIgnore]
public Plugin Owner { get; private set; }
[JsonIgnore]
public bool IsTemporary { get; private set; }
public ZoneFlags Flags { get; set; }
public Definition() { }
public Definition(Vector3 position)
{
Radius = 20f;
Location = position;
Flags = new ZoneFlags();
}
public void WithOwner(Plugin owner)
{
Owner = owner;
IsTemporary = true;
}
}
#endregion
}
#endregion
#region Entity Management
private void OnPlayerEnterZone(BasePlayer player, Zone zone)
{
if (!player || IsNpc(player))
return;
if (!zone.CanEnterZone(player))
{
EjectPlayer(player, zone);
return;
}
if (HasFlag(zone, ZoneFlags.Eject))
{
if (!CanBypass(player, ZoneFlags.Eject) && !IsAdmin(player))
{
EjectPlayer(player, zone);
return;
}
}
//if (HasFlag(zone, ZoneFlags.KeepVehiclesOut) && player.isMounted && ReverseVelocity(player.GetMountedVehicle()))
//{
// SendMessage(player, Message("novehiclesenter", player.UserIDString));
// return;
//}
if (player.IsSleeping() && !player.IsConnected)
{
if (HasFlag(zone, ZoneFlags.KillSleepers))
{
if (!CanBypass(player, ZoneFlags.KillSleepers) && !IsAdmin(player))
{
player.Die();
return;
}
}
if (HasFlag(zone, ZoneFlags.EjectSleepers))
{
if (!CanBypass(player, ZoneFlags.EjectSleepers) && !IsAdmin(player))
{
EjectPlayer(player, zone);
return;
}
}
}
if (HasFlag(zone, ZoneFlags.Kill))
{
if (!CanBypass(player, ZoneFlags.Kill) && !IsAdmin(player))
{
player.Die();
return;
}
}
if (!zonedPlayers.TryGetValue(player.userID, out EntityZones entityZones))
zonedPlayers[player.userID] = entityZones = new EntityZones();
if (!entityZones.EnterZone(zone))
return;
if (zone.parent)
entityZones.UpdateFlags();
else entityZones.AddFlags(zone.definition.Flags);
zone.OnPlayerEnterZone(player);
UpdateMetabolismForPlayer(player);
if (!string.IsNullOrEmpty(zone.definition.EnterMessage))
{
if (PopupNotifications != null && Configuration.Notifications.Popups)
PopupNotifications.Call("CreatePopupNotification", string.Format(zone.definition.EnterMessage, player.displayName), player);
else SendMessage(player, zone.definition.EnterMessage, player.displayName);
}
Interface.CallHook("OnEnterZone", zone.definition.Id, player);
}
private void OnPlayerExitZone(BasePlayer player, Zone zone)
{
if (!player || IsNpc(player))
return;
//if (HasFlag(zone, ZoneFlags.KeepVehiclesIn) && player.isMounted && ReverseVelocity(player.GetMountedVehicle()))
//{
// SendMessage(player, Message("novehiclesleave", player.UserIDString));
// return;
//}
if (!zone.CanLeaveZone(player))
{
AttractPlayer(player, zone);
return;
}
if (!zonedPlayers.TryGetValue(player.userID, out EntityZones entityZones))
return;
entityZones.LeaveZone(zone);
if (entityZones.ShouldRemove())
zonedPlayers.Remove(player.userID);
else entityZones.UpdateFlags();
zone.OnPlayerExitZone(player);
UpdateMetabolismForPlayer(player);
if (!string.IsNullOrEmpty(zone.definition.LeaveMessage))
{
if (PopupNotifications != null && Configuration.Notifications.Popups)
PopupNotifications.Call("CreatePopupNotification", string.Format(zone.definition.LeaveMessage, player.displayName), player);
else SendMessage(player, zone.definition.LeaveMessage, player.displayName);
}
Interface.CallHook("OnExitZone", zone.definition.Id, player);
}
private void UpdateMetabolismForPlayer(BasePlayer player)
{
if (!player)
return;
MetabolismAttribute bleeding = player.metabolism.bleeding;
if (HasPlayerFlag(player, ZoneFlags.NoBleed))
{
bleeding.value = 0f;
bleeding.max = 0f;
}
else bleeding.max = 1f;
MetabolismAttribute oxygen = player.metabolism.oxygen;
if (HasPlayerFlag(player, ZoneFlags.NoDrown))
{
oxygen.value = 1f;
oxygen.min = 1f;
}
else oxygen.min = 0f;
MetabolismAttribute poison = player.metabolism.poison;
if (HasPlayerFlag(player, ZoneFlags.NoPoison))
{
poison.value = 0f;
poison.max = 0f;
}
else poison.max = 100f;
MetabolismAttribute calories = player.metabolism.calories;
if (HasPlayerFlag(player, ZoneFlags
Merged post
I also modifed Restore upon death, but for some reason it isnt letting me post it here