C#:
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.Globalization;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Zone Manager", "k1lly0u", "3.0.23")]
[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<ulong, EntityZones> zonedPlayers = new Hash<ulong, EntityZones>();
private readonly Hash<uint, EntityZones> zonedEntities = new Hash<uint, EntityZones>();
private readonly Dictionary<ulong, string> lastPlayerZone = new Dictionary<ulong, string>();
private ZoneFlags globalFlags;
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;
lang.RegisterMessages(Messages, this);
permission.RegisterPermission(PERMISSION_ZONE, this);
foreach (object flag in Enum.GetValues(typeof(ZoneFlags))) permission.RegisterPermission(PERMISSION_IGNORE_FLAG + flag.ToString().ToLower(), this);
LoadData();
}
private void OnServerInitialized() {
InitializeZones();
InitializeUpdateBehaviour();
}
private void OnTerrainInitialized() => InitializeZones();
private void OnPlayerConnected(BasePlayer player) => updateBehaviour.QueueUpdate(player);
private void OnEntityKill(BaseEntity baseEntity) {
if (!baseEntity || !baseEntity.IsValid() || baseEntity.IsDestroyed) return;
EntityZones entityZones;
if (zonedEntities.TryGetValue(baseEntity.net.ID, out entityZones)) {
for (int i = entityZones.Zones.Count - 1; i >= 0; i--) {
entityZones.Zones.ElementAt(i)?.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.Destroy(kvp.Value.gameObject);
zones.Clear();
Instance = null;
}
#endregion
#region UpdateQueue
private UpdateBehaviour updateBehaviour;
private void InitializeUpdateBehaviour() {
updateBehaviour = new GameObject("ZoneManager.UpdateBehaviour").AddComponent<UpdateBehaviour>();
foreach (BasePlayer player in BasePlayer.activePlayerList) updateBehaviour.QueueUpdate(player);
}
private void DestroyUpdateBehaviour() => 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 System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
private Queue<BasePlayer> playerUpdateQueue = new Queue<BasePlayer>();
private const float MAX_MS = 0.25f;
private void OnDestroy() {
playerUpdateQueue.Clear();
}
internal void QueueUpdate(BasePlayer player) {
if (!playerUpdateQueue.Contains(player)) playerUpdateQueue.Enqueue(player);
}
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 gObject) {
BasePlayer player = planner?.GetOwnerPlayer();
if (!player) return;
BaseEntity entity = gObject?.ToBaseEntity();
if (!entity) return;
if (entity is BuildingBlock || entity is SimpleBuildingBlock) {
if (HasPlayerFlag(player, ZoneFlags.NoBuild, true)) {
entity.Invoke(() => entity.Kill(BaseNetworkable.DestroyMode.Gib), 0.1f);
SendMessage(player, Message("noBuild", player.UserIDString));
}
}
else {
if (entity is BuildingPrivlidge) {
if (HasPlayerFlag(player, ZoneFlags.NoCup, false)) {
entity.Invoke(() => entity.Kill(BaseNetworkable.DestroyMode.Gib), 0.1f);
SendMessage(player, Message("noCup", player.UserIDString));
}
}
else {
if (HasPlayerFlag(player, ZoneFlags.NoDeploy, true)) {
entity.Invoke(() => entity.Kill(BaseNetworkable.DestroyMode.Gib), 0.1f);
SendMessage(player, Message("noDeploy", player.UserIDString));
}
}
}
}
private object OnStructureUpgrade(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum grade) {
if (HasPlayerFlag(player, ZoneFlags.NoUpgrade, true)) {
SendMessage(player, Message("noUpgrade", player.UserIDString));
return true;
}
return null;
}
private void OnItemDeployed(Deployer deployer, BaseEntity deployedEntity) {
BasePlayer player = deployer.GetOwnerPlayer();
if (!player) return;
if (HasPlayerFlag(player, ZoneFlags.NoDeploy, true)) {
deployedEntity.Invoke(() => deployedEntity.Kill(BaseNetworkable.DestroyMode.Gib), 0.1f);
SendMessage(player, Message("noDeploy", player.UserIDString));
}
}
private void OnItemUse(Item item, int amount) {
BaseEntity entity = item?.parent?.entityOwner;
if (!entity) return;
if (entity is FlameTurret || entity is AutoTurret || entity is GunTrap) {
if (HasEntityFlag(entity, ZoneFlags.InfiniteTrapAmmo)) item.amount += amount;
return;
}
if (entity is SearchLight) {
if (HasEntityFlag(entity, ZoneFlags.AlwaysLights)) {
item.amount += amount;
return;
}
if (HasEntityFlag(entity, ZoneFlags.AutoLights)) {
if (TOD_Sky.Instance.Cycle.Hour > Instance.configData.AutoLights.OnTime || TOD_Sky.Instance.Cycle.Hour < Instance.configData.AutoLights.OffTime) item.amount += amount;
}
}
}
private void OnRunPlayerMetabolism(PlayerMetabolism metabolism, BaseCombatEntity ownerEntity, float delta) {
BasePlayer player = ownerEntity as BasePlayer;
if (!player) 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 = 1f;
}
private object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel) {
if (!player) return null;
if (HasPlayerFlag(player, ZoneFlags.NoChat, true)) {
SendMessage(player, Message("noChat", player.UserIDString));
return true;
}
return null;
}
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, true)) {
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, false)) {
SendMessage(player, Message("noSuicide", player.UserIDString));
return true;
}
return null;
}
private void OnPlayerDisconnected(BasePlayer player) {
if (!player) return;
if (HasPlayerFlag(player, ZoneFlags.KillSleepers, true)) {
player.Die();
return;
}
if (HasPlayerFlag(player, ZoneFlags.EjectSleepers, true)) {
EntityZones entityZones;
if (!zonedPlayers.TryGetValue(player.userID, out entityZones) || entityZones.Count == 0) return;
for (int i = 0; i < entityZones.Count; i++) {
Zone zone = entityZones.Zones.ElementAt(i);
if (!zone) continue;
if (HasFlag(zone, ZoneFlags.EjectSleepers)) {
EjectPlayer(player, zone);
return;
}
}
}
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo) {
if (!entity || entity.GetComponent<ResourceDispenser>() != null) return null;
BasePlayer attacker = hitinfo.InitiatorPlayer;
BasePlayer victim = entity as BasePlayer;
if (victim != null) {
if (hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Fall) {
if (HasPlayerFlag(victim, ZoneFlags.NoFallDamage, false)) return true;
}
if (victim.IsSleeping() && HasPlayerFlag(victim, ZoneFlags.SleepGod, false)) return true;
else if (attacker != null) {
if (IsNpc(victim)) return null;
if (HasPlayerFlag(victim, ZoneFlags.PvpGod, false)) {
if (attacker == victim && hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Suicide) {
if (HasPlayerFlag(victim, ZoneFlags.NoSuicide, false)) return true;
return null;
}
if (IsNpc(attacker) && configData.NPCHurtPvpGod) return null;
return true;
}
else if (HasPlayerFlag(attacker, ZoneFlags.PvpGod, false) && !IsNpc(attacker)) return true;
}
else if (HasPlayerFlag(victim, ZoneFlags.PveGod, false) && !IsNpc(victim)) return true;
else if (hitinfo.Initiator is FireBall && HasPlayerFlag(victim, ZoneFlags.PvpGod, false)) return true;
return null;
}
BaseNpc baseNpc = entity as BaseNpc;
if (baseNpc != null) {
if (HasEntityFlag(baseNpc, ZoneFlags.NoPve)) {
if (attacker != null && CanBypass(attacker, ZoneFlags.NoPve)) return null;
return true;
}
return null;
}
if (!(entity is LootContainer) && !(entity is BaseHelicopter)) {
if (HasEntityFlag(entity, ZoneFlags.UnDestr)) {
if (hitinfo.InitiatorPlayer != null && CanBypass(hitinfo.InitiatorPlayer, ZoneFlags.UnDestr)) return null;
if (hitinfo.damageTypes.GetMajorityDamageType() == DamageType.Decay && configData.DecayDamageUndestr) return null;
return true;
}
}
return null;
}
private void OnEntitySpawned(BaseNetworkable baseNetworkable) {
if (baseNetworkable is BaseEntity) timer.In(2, () => CanSpawn(baseNetworkable as BaseEntity));
}
private void CanSpawn(BaseEntity baseEntity) {
if (!baseEntity.IsValid() || baseEntity.IsDestroyed) return;
if (Interface.CallHook("CanSpawnInZone", baseEntity) != null) return;
if (baseEntity is BaseCorpse) {
if (HasEntityFlag(baseEntity, ZoneFlags.NoCorpse) && !CanBypass((baseEntity as BaseCorpse).OwnerID, ZoneFlags.NoCorpse)) baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
if (baseEntity is LootContainer || baseEntity is JunkPile) {
if (HasEntityFlag(baseEntity, ZoneFlags.NoLootSpawns)) baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
else if (baseEntity is BaseNpc || baseEntity is NPCPlayer) {
if (HasEntityFlag(baseEntity, ZoneFlags.NoNPCSpawns)) baseEntity.Invoke(() => baseEntity.Kill(BaseNetworkable.DestroyMode.None), 0.1f);
}
else if (baseEntity is DroppedItem || baseEntity is WorldItem) {
if (HasEntityFlag(baseEntity, ZoneFlags.NoDrop)) {
(baseEntity as WorldItem).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, false) ? (object)false : null;
private object CanUpdateSign(BasePlayer player, Signage sign) {
if (HasPlayerFlag(player, ZoneFlags.NoSignUpdates, false)) {
SendMessage(player, Message("noSignUpdates", player.UserIDString));
return false;
}
return null;
}
private object OnOvenToggle(BaseOven oven, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoOvenToggle, false)) {
SendMessage(player, Message("noOvenToggle", player.UserIDString));
return true;
}
return null;
}
private object CanUseVending(BasePlayer player, VendingMachine machine) {
if (HasPlayerFlag(player, ZoneFlags.NoVending, false)) {
SendMessage(player, Message("noVending", player.UserIDString));
return false;
}
return null;
}
private object CanHideStash(BasePlayer player, StashContainer stash) {
if (HasPlayerFlag(player, ZoneFlags.NoStash, false)) {
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 != null) {
if (HasPlayerFlag(player, ZoneFlags.NoCraft, false)) {
SendMessage(player, Message("noCraft", player.UserIDString));
return false;
}
}
return null;
}
private void OnDoorOpened(Door door, BasePlayer player) {
if (HasPlayerFlag(player, ZoneFlags.NoDoorAccess, false)) {
SendMessage(player, Message("noDoor", player.UserIDString));
door.CloseRequest();
}
}
#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))) {
if (looter == target && Backpacks != null) {
object hookResult = Backpacks.Call("CanLootPlayer", target, looter);
if (hookResult is bool && (bool)hookResult) 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) OnLootCorpse(entity as LootableCorpse, player);
if (entity is DroppedItemContainer) OnLootContainer(entity as DroppedItemContainer, player);
if (entity is StorageContainer) OnLootInternal(player, ZoneFlags.NoBoxLoot);
}
private object CanLootEntity(BasePlayer player, LootableCorpse corpse) {
if (corpse.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf, false)) return null;
return CanLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private void OnLootCorpse(LootableCorpse corpse, BasePlayer player) {
if (corpse.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf, false)) return;
OnLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private void OnLootContainer(DroppedItemContainer container, BasePlayer player) {
if (container.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf, false)) return;
OnLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private object CanLootEntity(BasePlayer player, DroppedItemContainer container) {
if (container.playerSteamID == player.userID && HasPlayerFlag(player, ZoneFlags.LootSelf, false)) return null;
return CanLootInternal(player, ZoneFlags.NoPlayerLoot);
}
private object CanLootEntity(BasePlayer player, StorageContainer container) => CanLootInternal(player, ZoneFlags.NoBoxLoot);
private object CanLootInternal(BasePlayer player, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
SendMessage(player, Message("noLoot", player.UserIDString));
return false;
}
return null;
}
private void OnLootInternal(BasePlayer player, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
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, ZoneFlags flag) {
if (HasPlayerFlag(player, flag, false)) {
SendMessage(player, Message("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 OnGrowableGather(GrowableEntity 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, Message("noGather", player.UserIDString));
return true;
}
}
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) {
if (HasPlayerFlag(entity?.ToPlayer(), ZoneFlags.NoAPCTargeting, false)) return false;
return null;
}
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) {
if (HasPlayerFlag(player, ZoneFlags.NoHeliTargeting, false)) return false;
return null;
}
private object OnHelicopterTarget(HelicopterTurret turret, BaseCombatEntity entity) => OnTargetPlayerInternal(entity?.ToPlayer(), ZoneFlags.NoHeliTargeting);
private object OnNpcTarget(BaseCombatEntity entity, BasePlayer player) => OnTargetPlayerInternal(player, ZoneFlags.NoNPCTargeting);
private object OnTargetPlayerInternal(BasePlayer player, ZoneFlags flag) {
if (player != null) {
if (HasPlayerFlag(player, flag, false)) return true;
}
return null;
}
#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) {
if (HasPlayerFlag(player, ZoneFlags.KillSleepers, true)) {
player.Invoke(() => KillSleepingPlayer(player), 3f);
return;
}
if (HasPlayerFlag(player, ZoneFlags.EjectSleepers, true)) {
player.Invoke(() => {
if (!player || !player.IsSleeping()) return;
EntityZones entityZones;
if (!zonedPlayers.TryGetValue(player.userID, out entityZones) || entityZones.Count == 0) return;
for (int i = 0; i < entityZones.Count; i++) {
Zone zone = entityZones.Zones.ElementAt(i);
if (!zone) return;
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, true)) {
if (player.IsConnected) OnPlayerSleep(player);
else player.Die();
}
}
private void UpdatePlayerZones(BasePlayer player) {
if (!player) return;
EntityZones entityZones;
if (zonedPlayers.TryGetValue(player.userID, out entityZones)) {
for (int i = entityZones.Count - 1; i >= 0; i--) {
Zone zone = entityZones.Zones.ElementAt(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);
}
}
}
for (int i = 0; i < zones.Count; i++) {
Zone zone = zones.ElementAt(i).Value;
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
#endregion
#region Zone Functions
private void InitializeZones() {
if (zonesInitialized) return;
foreach (Zone.Definition definition in storedData.definitions) CreateZone(definition);
foreach (Zone zone in zones.Values) zone.FindZoneParent();
zonesInitialized = true;
UnsubscribeAll();
UpdateHookSubscriptions();
}
private void CreateZone(Zone.Definition definition) {
Zone zone = new GameObject().AddComponent<Zone>();
zone.InitializeZone(definition);
zones.Add(definition.Id, zone);
}
private bool ReverseVelocity(BasePlayer player) {
BaseVehicle baseVehicle = player.GetMounted().VehicleParent();
if (baseVehicle != null) {
Vector3 euler = baseVehicle.transform.eulerAngles;
baseVehicle.transform.rotation = Quaternion.Euler(euler.x, euler.y - 180f, euler.z);
baseVehicle.rigidBody.velocity *= -1f;
return true;
}
return false;
}
private void EjectPlayer(BasePlayer player, Zone zone) {
if (zone.keepInList.Contains(player.userID) || zone.whitelist.Contains(player.userID)) return;
if (!string.IsNullOrEmpty(zone.definition.Permission)) {
if (HasPermission(player, zone.definition.Permission)) return;
}
if (player.isMounted && ReverseVelocity(player)) {
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) position = (Vector3)success;
}
if (position == Vector3.zero) {
float distance;
if (zone.definition.Size != Vector3.zero) distance = Mathf.Max(zone.definition.Size.x, zone.definition.Size.z);
else distance = zone.definition.Radius;
position = zone.transform.position + (((player.transform.position.XZ3D() - zone.transform.position.XZ3D()).normalized) * (distance + 10f));
RaycastHit rayHit;
if (Physics.Raycast(new Ray(new Vector3(position.x, position.y + 300, position.z), Vector3.down), out 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)) {
SendMessage(player, Message("attract", player.UserIDString));
return;
}
float distance;
if (zone.definition.Size != Vector3.zero) distance = Mathf.Max(zone.definition.Size.x, zone.definition.Size.z);
else distance = 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 {
internal Definition definition;
internal ZoneFlags disabledFlags = ZoneFlags.None;
internal Zone parent;
internal List<BasePlayer> players = Pool.GetList<BasePlayer>();
internal List<BaseEntity> entities = Pool.GetList<BaseEntity>();
internal List<ulong> keepInList = Pool.GetList<ulong>();
internal List<ulong> whitelist = Pool.GetList<ulong>();
private Rigidbody rigidbody;
internal Collider collider;
internal Bounds colliderBounds;
private ChildSphereTrigger<TriggerRadiation> radiation;
private ChildSphereTrigger<TriggerComfort> comfort;
private ChildSphereTrigger<TriggerTemperature> temperature;
private TriggerSafeZone safeZone;
private bool isTogglingLights = false;
private void Awake() {
gameObject.layer = (int)Layer.Reserved1;
gameObject.name = "ZoneManager";
enabled = false;
}
private void OnDestroy() {
EmptyZone();
Pool.FreeList(ref players);
Pool.FreeList(ref entities);
Pool.FreeList(ref keepInList);
Pool.FreeList(ref whitelist);
}
private void EmptyZone() {
RemoveAllPlayers();
keepInList.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) {
this.definition = definition;
transform.position = definition.Location;
transform.rotation = Quaternion.Euler(definition.Rotation);
if (definition.Enabled) {
RegisterPermission();
InitializeCollider();
InitializeAutoLights();
InitializeRadiation();
InitializeSafeZone();
InitializeComfort();
InitializeTemperature();
RemoveAllPlayers();
AddAllPlayers();
}
else {
InvokeHandler.CancelInvoke(this, CheckAlwaysLights);
InvokeHandler.CancelInvoke(this, CheckLights);
if (isLightsOn) ServerMgr.Instance.StartCoroutine(ToggleLights(false));
EmptyZone();
if (collider != null) DestroyImmediate(collider);
if (rigidbody != null) DestroyImmediate(rigidbody);
}
enabled = definition.Enabled;
}
public void FindZoneParent() {
if (string.IsNullOrEmpty(definition.ParentID)) 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);
}
private void RegisterPermission() {
if (!string.IsNullOrEmpty(definition.Permission) && !Instance.permission.PermissionExists(definition.Permission)) Instance.permission.RegisterPermission(definition.Permission, Instance);
}
private void InitializeCollider() {
if (collider != null) DestroyImmediate(collider);
if (rigidbody != null) 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 != null) Destroy(sphereCollider);
if (!boxCollider) {
boxCollider = gameObject.AddComponent<BoxCollider>();
boxCollider.isTrigger = true;
}
boxCollider.size = definition.Size;
colliderBounds = boxCollider.bounds;
collider = boxCollider;
}
else {
if (boxCollider != null) 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) {
if (radiation == null) 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) {
if (comfort == null) 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) {
if (temperature == null) 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) {
if (safeZone == null) safeZone = gameObject.AddComponent<TriggerSafeZone>();
safeZone.interestLayers = PLAYER_MASK;
safeZone.enabled = this.enabled;
}
else {
if (safeZone != null) Destroy(safeZone);
}
}
private void AddToTrigger(TriggerBase triggerBase, BasePlayer player) {
if (!triggerBase || !player) return;
if (triggerBase.entityContents == null) triggerBase.entityContents = new HashSet<BaseEntity>();
if (!triggerBase.entityContents.Contains(player)) {
triggerBase.entityContents.Add(player);
player.EnterTrigger(triggerBase);
if (triggerBase is TriggerSafeZone) {
if (player.IsItemHoldRestricted(player.inventory.containerBelt.FindItemByUID(player.svActiveItemID))) player.UpdateActiveItem(0);
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)) {
triggerBase.entityContents.Remove(player);
player.LeaveTrigger(triggerBase);
if (triggerBase is TriggerSafeZone) {
if (!player.InSafeZone()) player.SetPlayerFlag(BasePlayer.PlayerFlags.SafeZone, false);
}
}
}
private void AddAllPlayers() {
for (int i = 0; i < players.Count; i++) {
BasePlayer player = players[i];
AddToTrigger(safeZone, player);
if (radiation != null) AddToTrigger(radiation.Trigger, player);
if (comfort != null) AddToTrigger(comfort.Trigger, player);
if (temperature != null) AddToTrigger(temperature.Trigger, player);
}
}
private void RemoveAllPlayers() {
for (int i = 0; i < players.Count; i++) {
BasePlayer player = players[i];
RemoveFromTrigger(safeZone, 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 > Instance.configData.AutoLights.OnTime || currentTime < Instance.configData.AutoLights.OffTime;
if (shouldBeActive != isLightsOn) {
isLightsOn = shouldBeActive;
ServerMgr.Instance.StartCoroutine(ToggleLights(isLightsOn));
}
}
private IEnumerator ToggleLights(bool active) {
while (isTogglingLights) yield return null;
isTogglingLights = true;
bool requiresFuel = Instance.configData.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 != null) {
if (active) {
if (!baseOven.IsOn()) {
if ((requiresFuel && baseOven.FindBurnable() != null) || !requiresFuel) baseOven.SetFlag(Ba
// ... [Code preview truncated due to length. Full source in attached file] ...