C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//Requires: ZoneManager
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
[Info("Dynamic PVP", "CatMeat/Arainrr", "4.2.13", ResourceId = 2728)]
[Description("Creates temporary PvP zones on certain actions/events")]
public class DynamicPVP : RustPlugin {
#region Fields
[PluginReference]
private readonly Plugin ZoneManager, BotReSpawn;
private const string PermissionAdmin = "dynamicpvp.admin";
private const string PrefabSphere = "assets/prefabs/visualization/sphere.prefab";
private const string ZoneName = "DynamicPVP";
private static object _true, _false;
private readonly Dictionary<string, Timer> _eventTimers = new Dictionary<string, Timer>();
private readonly Dictionary<ulong, LeftZone> _pvpDelays = new Dictionary<ulong, LeftZone>();
private readonly Dictionary<string, string> _activeDynamicZones = new Dictionary<string, string>();
//ID -> EventName
private bool _dataChanged;
private Vector3 _oilRigPosition;
private Vector3 _largeOilRigPosition;
private Coroutine _createEventsCoroutine;
public static DynamicPVP Instance {
get;
private set;
}
private class LeftZone : Pool.IPooled {
public string zoneId;
public string eventName;
public Timer zoneTimer;
public void EnterPool() {
zoneId = null;
eventName = null;
zoneTimer?.Destroy();
zoneTimer = null;
}
public void LeavePool() {
}
}
[Flags]
[JsonConverter(typeof(StringEnumConverter))]
private enum PVPDelayFlags {
None = 0, ZonePlayersCanDamageDelayedPlayers = 1, DelayedPlayersCanDamageZonePlayers = 1 << 1, DelayedPlayersCanDamageDelayedPlayers = 1 << 2
}
private enum GeneralEventType {
Bradley, Helicopter, SupplyDrop, SupplySignal, CargoShip, HackableCrate, ExcavatorIgnition
}
#endregion Fields
#region Oxide Hooks
private void Init() {
Instance = this;
LoadData();
_true = true;
_false = false;
permission.RegisterPermission(PermissionAdmin, this);
AddCovalenceCommand(configData.Chat.Command, nameof(CmdDynamicPVP));
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(OnCargoPlaneSignaled));
Unsubscribe(nameof(OnCrateHack));
Unsubscribe(nameof(OnDieselEngineToggled));
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnLootEntity));
Unsubscribe(nameof(OnCrateHackEnd));
Unsubscribe(nameof(OnSupplyDropLanded));
Unsubscribe(nameof(OnEntityKill));
Unsubscribe(nameof(OnPlayerCommand));
Unsubscribe(nameof(OnServerCommand));
Unsubscribe(nameof(CanEntityTakeDamage));
Unsubscribe(nameof(OnEnterZone));
Unsubscribe(nameof(OnExitZone));
if (configData.Global.LogToFile) {
_debugStringBuilder = new StringBuilder();
}
}
private void OnServerInitialized() {
//Pool.FillBuffer<LeftZone>();
DeleteOldDynamicZones();
_createEventsCoroutine = ServerMgr.Instance.StartCoroutine(CreateMonumentEvents());
if (configData.GeneralEvents.ExcavatorIgnition.Enabled) {
Subscribe(nameof(OnDieselEngineToggled));
}
if (configData.GeneralEvents.PatrolHelicopter.Enabled || configData.GeneralEvents.BradleyApc.Enabled) {
Subscribe(nameof(OnEntityDeath));
}
if (configData.GeneralEvents.SupplySignal.Enabled || configData.GeneralEvents.TimedSupply.Enabled) {
Subscribe(nameof(OnCargoPlaneSignaled));
}
if (configData.GeneralEvents.HackableCrate.Enabled && configData.GeneralEvents.HackableCrate.TimerStartWhenUnlocked) {
Subscribe(nameof(OnCrateHackEnd));
}
if (configData.GeneralEvents.TimedSupply.Enabled && configData.GeneralEvents.TimedSupply.TimerStartWhenLooted || configData.GeneralEvents.SupplySignal.Enabled && configData.GeneralEvents.SupplySignal.TimerStartWhenLooted || configData.GeneralEvents.HackableCrate.Enabled && configData.GeneralEvents.HackableCrate.TimerStartWhenLooted) {
Subscribe(nameof(OnLootEntity));
}
if (configData.GeneralEvents.HackableCrate.Enabled && !configData.GeneralEvents.HackableCrate.StartWhenSpawned) {
Subscribe(nameof(OnCrateHack));
}
if (configData.GeneralEvents.TimedSupply.Enabled && !configData.GeneralEvents.TimedSupply.StartWhenSpawned || configData.GeneralEvents.SupplySignal.Enabled && !configData.GeneralEvents.SupplySignal.StartWhenSpawned) {
Subscribe(nameof(OnSupplyDropLanded));
}
if (configData.GeneralEvents.TimedSupply.Enabled && configData.GeneralEvents.TimedSupply.StartWhenSpawned || configData.GeneralEvents.SupplySignal.Enabled && configData.GeneralEvents.SupplySignal.StartWhenSpawned || configData.GeneralEvents.HackableCrate.Enabled && configData.GeneralEvents.HackableCrate.StartWhenSpawned) {
Subscribe(nameof(OnEntitySpawned));
}
if (configData.GeneralEvents.TimedSupply.Enabled && configData.GeneralEvents.TimedSupply.StopWhenKilled || configData.GeneralEvents.SupplySignal.Enabled && configData.GeneralEvents.SupplySignal.StopWhenKilled || configData.GeneralEvents.HackableCrate.Enabled && configData.GeneralEvents.HackableCrate.StopWhenKilled) {
Subscribe(nameof(OnEntityKill));
}
if (configData.GeneralEvents.CargoShip.Enabled) {
Subscribe(nameof(OnEntityKill));
Subscribe(nameof(OnEntitySpawned));
foreach (var serverEntity in BaseNetworkable.serverEntities) {
var cargoShip = serverEntity as CargoShip;
if (cargoShip == null) {
continue;
}
OnEntitySpawned(cargoShip);
}
}
}
private void Unload() {
if (_createEventsCoroutine != null) {
ServerMgr.Instance.StopCoroutine(_createEventsCoroutine);
}
if (_activeDynamicZones.Count > 0) {
PrintDebug($"Deleting {_activeDynamicZones.Count} active zones.", true);
foreach (var entry in _activeDynamicZones.ToArray()) {
DeleteDynamicZone(entry.Key);
}
}
var leftZones = _pvpDelays.Values.ToArray();
for (var i = leftZones.Length - 1; i >= 0; i--) {
var value = leftZones[i];
Pool.Free(ref value);
}
var spheres = _zoneSpheres.Values.ToArray();
for (var i = spheres.Length - 1; i >= 0; i--) {
var sphereEntities = spheres[i];
foreach (var sphereEntity in sphereEntities) {
if (sphereEntity != null && !sphereEntity.IsDestroyed) {
sphereEntity.KillMessage();
}
}
Pool.FreeList(ref sphereEntities);
}
SaveData();
SaveDebug();
Pool.directory.Remove(typeof(LeftZone));
_true = _false = null;
Instance = null;
}
private void OnServerSave() {
timer.Once(Random.Range(0f, 60f), () => {
SaveDebug();
if (_dataChanged) {
SaveData();
_dataChanged = false;
}
}
);
}
private void OnPlayerRespawned(BasePlayer player) {
if (player == null || !player.userID.IsSteamId()) {
return;
}
TryRemovePVPDelay(player.userID, player.displayName);
}
#endregion Oxide Hooks
#region Methods
private void TryRemoveEventTimer(string zoneId) {
Timer value;
if (_eventTimers.TryGetValue(zoneId, out value)) {
value?.Destroy();
_eventTimers.Remove(zoneId);
}
}
private LeftZone GetOrAddPVPDelay(BasePlayer player, string zoneId, string eventName) {
PrintDebug($"Adding {player.displayName} to pvp delay.");
var added = false;
LeftZone leftZone;
if (_pvpDelays.TryGetValue(player.userID, out leftZone)) {
leftZone.zoneTimer?.Destroy();
}
else {
added = true;
leftZone = Pool.Get<LeftZone>();
_pvpDelays.Add(player.userID, leftZone);
}
leftZone.zoneId = zoneId;
leftZone.eventName = eventName;
if (added) {
CheckHooks(true);
}
return leftZone;
}
private void TryRemovePVPDelay(ulong playerId, string playerName) {
PrintDebug($"Removing {playerName} from pvp delay.");
LeftZone leftZone;
if (_pvpDelays.TryGetValue(playerId, out leftZone)) {
_pvpDelays.Remove(playerId);
Interface.CallHook("OnPlayerRemovedFromPVPDelay", playerId, leftZone.zoneId);
Pool.Free(ref leftZone);
CheckHooks(true);
}
}
private bool CheckEntityOwner(BaseEntity baseEntity) {
if (configData.Global.CheckEntityOwner && baseEntity.OwnerID.IsSteamId()) {
PrintDebug($"{baseEntity} is owned by the player({baseEntity.OwnerID}).Skipping event creation.");
return false;
}
return true;
}
private bool CanCreateDynamicPVP(string eventName, BaseEntity entity) {
if (Interface.CallHook("OnCreateDynamicPVP", eventName, entity) != null) {
PrintDebug($"There are other plugins that prevent {eventName} events from being created.", true);
return false;
}
return true;
}
private void CheckHooks(bool pvpDelay = false) {
if (pvpDelay) {
if (_pvpDelays.Count > 0) {
Subscribe(nameof(CanEntityTakeDamage));
}
else {
Unsubscribe(nameof(CanEntityTakeDamage));
}
}
else {
if (_activeDynamicZones.Count > 0) {
Subscribe(nameof(OnEnterZone));
Subscribe(nameof(OnExitZone));
}
else {
Unsubscribe(nameof(OnEnterZone));
Unsubscribe(nameof(OnExitZone));
}
}
var hasCommands = false;
foreach (var eventName in _activeDynamicZones.Values) {
var baseEvent = GetBaseEvent(eventName);
if (baseEvent != null && baseEvent.CommandList.Count > 0) {
hasCommands = true;
break;
}
}
if (!hasCommands) {
foreach (var leftZone in _pvpDelays.Values) {
var baseEvent = GetBaseEvent(leftZone.eventName);
if (baseEvent != null && baseEvent.CommandList.Count > 0 && baseEvent.CommandWorksForPVPDelay) {
hasCommands = true;
break;
}
}
}
if (hasCommands) {
Subscribe(nameof(OnPlayerCommand));
Subscribe(nameof(OnServerCommand));
}
else {
Unsubscribe(nameof(OnPlayerCommand));
Unsubscribe(nameof(OnServerCommand));
}
}
private BaseEvent GetBaseEvent(string eventName) {
if (string.IsNullOrEmpty(eventName)) {
throw new ArgumentNullException(nameof(eventName));
}
var externalEvent = Interface.CallHook("OnGetBaseEvent", eventName) as BaseEvent;
if (externalEvent != null) {
return externalEvent;
}
if (Enum.IsDefined(typeof(GeneralEventType), eventName)) {
GeneralEventType generalEventType;
if (Enum.TryParse(eventName, true, out generalEventType)) {
switch (generalEventType) {
case GeneralEventType.Bradley : return configData.GeneralEvents.BradleyApc;
case GeneralEventType.HackableCrate : return configData.GeneralEvents.HackableCrate;
case GeneralEventType.Helicopter : return configData.GeneralEvents.PatrolHelicopter;
case GeneralEventType.SupplyDrop : return configData.GeneralEvents.TimedSupply;
case GeneralEventType.SupplySignal : return configData.GeneralEvents.SupplySignal;
case GeneralEventType.ExcavatorIgnition : return configData.GeneralEvents.ExcavatorIgnition;
case GeneralEventType.CargoShip : return configData.GeneralEvents.CargoShip;
default : PrintDebug($"ERROR: Unknown GeneralEventType: {generalEventType} | {eventName}.", error : true);
break;
}
}
}
AutoEvent autoEvent;
if (storedData.autoEvents.TryGetValue(eventName, out autoEvent)) {
return autoEvent;
}
TimedEvent timedEvent;
if (storedData.timedEvents.TryGetValue(eventName, out timedEvent)) {
return timedEvent;
}
MonumentEvent monumentEvent;
if (configData.MonumentEvents.TryGetValue(eventName, out monumentEvent)) {
return monumentEvent;
}
PrintDebug($"ERROR: Failed to get base event settings for {eventName}.", error : true);
return null;
}
#endregion Methods
#region Events
#region General Event
#region ExcavatorIgnition Event
private void OnDieselEngineToggled(DieselEngine dieselEngine) {
if (dieselEngine == null || dieselEngine.net == null) {
return;
}
var zoneId = dieselEngine.net.ID.ToString();
if (dieselEngine.IsOn()) {
DeleteDynamicZone(zoneId);
HandleGeneralEvent(GeneralEventType.ExcavatorIgnition, dieselEngine, true);
}
else {
HandleDeleteDynamicZone(zoneId);
}
}
#endregion ExcavatorIgnition Event
#region HackableLockedCrate Event
private void OnEntitySpawned(HackableLockedCrate hackableLockedCrate) {
if (hackableLockedCrate == null || hackableLockedCrate.net == null) {
return;
}
if (!configData.GeneralEvents.HackableCrate.Enabled || !configData.GeneralEvents.HackableCrate.StartWhenSpawned) {
return;
}
PrintDebug("Trying to create the event when the hackable locked crate is spawned.");
NextTick(() => LockedCrateEvent(hackableLockedCrate));
}
private void OnCrateHack(HackableLockedCrate hackableLockedCrate) {
if (hackableLockedCrate == null || hackableLockedCrate.net == null) {
return;
}
PrintDebug("Trying to create the event when the hackable locked crate is starting hack.");
NextTick(() => LockedCrateEvent(hackableLockedCrate));
}
private void OnCrateHackEnd(HackableLockedCrate hackableLockedCrate) {
if (hackableLockedCrate == null || hackableLockedCrate.net == null) {
return;
}
HandleDeleteDynamicZone(hackableLockedCrate.net.ID.ToString(), configData.GeneralEvents.HackableCrate.Duration, GeneralEventType.HackableCrate.ToString());
}
private void OnLootEntity(BasePlayer player, HackableLockedCrate hackableLockedCrate) {
if (hackableLockedCrate == null || hackableLockedCrate.net == null) {
return;
}
if (!configData.GeneralEvents.HackableCrate.Enabled || !configData.GeneralEvents.HackableCrate.TimerStartWhenLooted) {
return;
}
HandleDeleteDynamicZone(hackableLockedCrate.net.ID.ToString(), configData.GeneralEvents.HackableCrate.Duration, GeneralEventType.HackableCrate.ToString());
}
private void OnEntityKill(HackableLockedCrate hackableLockedCrate) {
if (hackableLockedCrate == null || hackableLockedCrate.net == null) {
return;
}
if (!configData.GeneralEvents.HackableCrate.Enabled || !configData.GeneralEvents.HackableCrate.StopWhenKilled) {
return;
}
var zoneId = hackableLockedCrate.net.ID.ToString();
//When the timer starts, don't stop the event immediately
if (!_eventTimers.ContainsKey(zoneId)) {
HandleDeleteDynamicZone(hackableLockedCrate.net.ID.ToString());
}
}
private void LockedCrateEvent(HackableLockedCrate hackableLockedCrate) {
if (!CheckEntityOwner(hackableLockedCrate)) {
return;
}
if (configData.GeneralEvents.HackableCrate.ExcludeOilRig && IsOnTheOilRig(hackableLockedCrate)) {
PrintDebug("The hackable locked crate is on the oil rig.Skipping event creation.");
return;
}
if (configData.GeneralEvents.HackableCrate.ExcludeCargoShip && IsOnTheCargoShip(hackableLockedCrate)) {
PrintDebug("The hackable locked crate is on the cargo ship.Skipping event creation.");
return;
}
HandleGeneralEvent(GeneralEventType.HackableCrate, hackableLockedCrate, true);
}
private bool IsOnTheCargoShip(HackableLockedCrate hackableLockedCrate) {
return hackableLockedCrate.GetComponentInParent<CargoShip>() != null;
}
private bool IsOnTheOilRig(HackableLockedCrate hackableLockedCrate) {
if (_oilRigPosition != Vector3.zero && Vector3Ex.Distance2D(hackableLockedCrate.transform.position, _oilRigPosition) < 50f) {
return true;
}
if (_largeOilRigPosition != Vector3.zero && Vector3Ex.Distance2D(hackableLockedCrate.transform.position, _largeOilRigPosition) < 50f) {
return true;
}
return false;
}
#endregion HackableLockedCrate Event
#region BaseHelicopter And BradleyAPC Event
private void OnEntityDeath(BaseHelicopter baseHelicopter, HitInfo info) {
if (baseHelicopter == null || baseHelicopter.net == null) {
return;
}
PatrolHelicopterEvent(baseHelicopter);
}
private void OnEntityDeath(BradleyAPC bradleyApc, HitInfo info) {
if (bradleyApc == null || bradleyApc.net == null) {
return;
}
BradleyApcEvent(bradleyApc);
}
private void PatrolHelicopterEvent(BaseHelicopter baseHelicopter) {
if (!configData.GeneralEvents.PatrolHelicopter.Enabled) {
return;
}
PrintDebug("Trying to create the event when the helicopter is dead.");
if (!CheckEntityOwner(baseHelicopter)) {
return;
}
HandleGeneralEvent(GeneralEventType.Helicopter, baseHelicopter, false);
}
private void BradleyApcEvent(BradleyAPC bradleyAPC) {
if (!configData.GeneralEvents.BradleyApc.Enabled) {
return;
}
PrintDebug("Trying to create the event when the bradley apc is dead.");
if (!CheckEntityOwner(bradleyAPC)) {
return;
}
HandleGeneralEvent(GeneralEventType.Bradley, bradleyAPC, false);
}
#endregion BaseHelicopter And BradleyAPC Event
#region SupplyDrop And SupplySignal Event
private readonly Dictionary<Vector3, Timer> activeSupplySignals = new Dictionary<Vector3, Timer>();
private void OnCargoPlaneSignaled(CargoPlane cargoPlane, SupplySignal supplySignal) {
NextTick(() => {
if (supplySignal == null || cargoPlane == null) {
return;
}
var dropPosition = cargoPlane.dropPosition;
if (activeSupplySignals.ContainsKey(dropPosition)) {
return;
}
activeSupplySignals.Add(dropPosition, timer.Once(900f, () => activeSupplySignals.Remove(dropPosition)));
PrintDebug($"A supply signal is thrown at {dropPosition}");
}
);
}
private void OnEntitySpawned(SupplyDrop supplyDrop) {
NextTick(() => OnSupplyDropEvent(supplyDrop, false));
}
private void OnSupplyDropLanded(SupplyDrop supplyDrop) {
if (supplyDrop == null || supplyDrop.net == null) {
return;
}
if (_activeDynamicZones.ContainsKey(supplyDrop.net.ID.ToString())) {
return;
}
NextTick(() => OnSupplyDropEvent(supplyDrop, true));
}
private void OnLootEntity(BasePlayer player, SupplyDrop supplyDrop) {
if (supplyDrop == null || supplyDrop.net == null) {
return;
}
var zoneId = supplyDrop.net.ID.ToString();
string eventName;
if (_activeDynamicZones.TryGetValue(zoneId, out eventName)) {
switch (eventName) {
case nameof(GeneralEventType.SupplySignal) : if (!configData.GeneralEvents.SupplySignal.Enabled || !configData.GeneralEvents.SupplySignal.TimerStartWhenLooted) {
return;
}
HandleDeleteDynamicZone(zoneId, configData.GeneralEvents.SupplySignal.Duration, eventName);
break;
case nameof(GeneralEventType.SupplyDrop) : if (!configData.GeneralEvents.TimedSupply.Enabled || !configData.GeneralEvents.TimedSupply.TimerStartWhenLooted) {
return;
}
HandleDeleteDynamicZone(zoneId, configData.GeneralEvents.TimedSupply.Duration, eventName);
break;
default : return;
}
}
}
private void OnEntityKill(SupplyDrop supplyDrop) {
if (supplyDrop == null || supplyDrop.net == null) {
return;
}
var zoneId = supplyDrop.net.ID.ToString();
string eventName;
if (_activeDynamicZones.TryGetValue(zoneId, out eventName)) {
switch (eventName) {
case nameof(GeneralEventType.SupplySignal) : if (!configData.GeneralEvents.SupplySignal.Enabled || !configData.GeneralEvents.SupplySignal.StopWhenKilled) {
return;
}
break;
case nameof(GeneralEventType.SupplyDrop) : if (!configData.GeneralEvents.TimedSupply.Enabled || !configData.GeneralEvents.TimedSupply.StopWhenKilled) {
return;
}
break;
default : return;
}
//When the timer starts, don't stop the event immediately
if (!_eventTimers.ContainsKey(zoneId)) {
HandleDeleteDynamicZone(zoneId);
}
}
}
private void OnSupplyDropEvent(SupplyDrop supplyDrop, bool isLanded) {
if (supplyDrop == null || supplyDrop.net == null) {
return;
}
PrintDebug($"Trying to create the event when the supply drop is {(isLanded ? "landed" : "spawned")} at {supplyDrop.transform.position}.");
if (!CheckEntityOwner(supplyDrop)) {
return;
}
Action action = null;
var isFromSupplySignal = IsProbablySupplySignal(supplyDrop.transform.position, ref action);
PrintDebug($"Supply drop is from supply signal: {isFromSupplySignal}");
if (isFromSupplySignal) {
if (!configData.GeneralEvents.SupplySignal.Enabled) {
PrintDebug("Event for supply signals disabled.Skipping event creation.");
return;
}
if (isLanded ? configData.GeneralEvents.SupplySignal.StartWhenSpawned : !configData.GeneralEvents.SupplySignal.StartWhenSpawned) {
PrintDebug($"{(isLanded ? "Landed" : "Spawned")} for supply signals disabled.");
return;
}
action?.Invoke();
HandleGeneralEvent(GeneralEventType.SupplySignal, supplyDrop, true);
}
else {
if (!configData.GeneralEvents.TimedSupply.Enabled) {
PrintDebug("Event for timed supply disabled.Skipping event creation.");
return;
}
if (isLanded ? configData.GeneralEvents.TimedSupply.StartWhenSpawned : !configData.GeneralEvents.TimedSupply.StartWhenSpawned) {
PrintDebug($"{(isLanded ? "Landed" : "Spawned")} for timed supply disabled.");
return;
}
HandleGeneralEvent(GeneralEventType.SupplyDrop, supplyDrop, true);
}
}
private bool IsProbablySupplySignal(Vector3 position, ref Action action) {
PrintDebug($"Checking {activeSupplySignals.Count} active supply signals");
if (activeSupplySignals.Count > 0) {
foreach (var entry in activeSupplySignals) {
var distance = Vector3Ex.Distance2D(entry.Key, position);
PrintDebug($"Found a supply signal at {entry.Key} located {distance}m away.");
if (distance <= configData.Global.CompareRadius) {
action = () => {
entry.Value?.Destroy();
activeSupplySignals.Remove(entry.Key);
PrintDebug($"Removing Supply signal from active list.Active supply signals remaining: {activeSupplySignals.Count}");
}
;
PrintDebug("Found matching a supply signal.");
return true;
}
}
PrintDebug("No matches found, probably from a timed event cargo plane");
return false;
}
PrintDebug("No active signals, must be from a timed event cargo plane");
return false;
}
#endregion SupplyDrop And SupplySignal Event
#region CargoShip Event
private void OnEntitySpawned(CargoShip cargoShip) {
if (cargoShip == null || cargoShip.net == null) {
return;
}
if (!configData.GeneralEvents.CargoShip.Enabled) {
return;
}
PrintDebug("Trying to create the event when the cargo ship is spawned.");
if (!CheckEntityOwner(cargoShip)) {
return;
}
var eventName = GeneralEventType.CargoShip.ToString();
if (!CanCreateDynamicPVP(eventName, cargoShip)) {
return;
}
NextTick(() => HandleParentedEntityEvent(eventName, cargoShip));
}
private void OnEntityKill(CargoShip cargoShip) {
if (cargoShip == null || cargoShip.net == null) {
return;
}
if (!configData.GeneralEvents.CargoShip.Enabled) {
return;
}
HandleDeleteDynamicZone(cargoShip.net.ID.ToString());
}
#endregion CargoShip Event
#endregion General Event
#region Monument Event
private IEnumerator CreateMonumentEvents() {
var changed = false;
var createdEvents = new List<string>();
foreach (var landmarkInfo in TerrainMeta.Path.Landmarks) {
if (!landmarkInfo.shouldDisplayOnMap) {
continue;
}
var monumentName = landmarkInfo.displayPhrase.english.Replace("\n", "");
if (string.IsNullOrEmpty(monumentName)) {
continue;
}
switch (landmarkInfo.name) {
case "OilrigAI" : _oilRigPosition = landmarkInfo.transform.position;
break;
case "OilrigAI2" : _largeOilRigPosition = landmarkInfo.transform.position;
break;
case "assets/bundled/prefabs/autospawn/monument/harbor/harbor_1.prefab" : monumentName += " A";
break;
case "assets/bundled/prefabs/autospawn/monument/harbor/harbor_2.prefab" : monumentName += " B";
break;
//case "assets/bundled/prefabs/autospawn/monument/harbor/fishing_village_b.prefab": monumentName += " A"; break;
//case "assets/bundled/prefabs/autospawn/monument/harbor/fishing_village_c.prefab": monumentName += " B"; break;
}
MonumentEvent monumentEvent;
if (!configData.MonumentEvents.TryGetValue(monumentName, out monumentEvent)) {
changed = true;
monumentEvent = new MonumentEvent();
configData.MonumentEvents.Add(monumentName, monumentEvent);
Puts($"A new monument {monumentName} was found and added to the config.");
}
if (monumentEvent.Enabled) {
if (HandleMonumentEvent(monumentName, landmarkInfo.transform, monumentEvent)) {
createdEvents.Add(monumentName);
}
yield return CoroutineEx.waitForSeconds(0.5f);
}
}
if (changed) {
SaveConfig();
}
if (createdEvents.Count > 0) {
PrintDebug($"{createdEvents.Count}({string.Join(", ", createdEvents)}) monument events were successfully created.", true);
}
createdEvents.Clear();
foreach (var entry in storedData.autoEvents) {
if (entry.Value.AutoStart) {
if (CreateDynamicZone(entry.Key, entry.Value.Position, entry.Value.ZoneId)) {
createdEvents.Add(entry.Key);
}
yield return CoroutineEx.waitForSeconds(0.5f);
}
}
if (createdEvents.Count > 0) {
PrintDebug($"{createdEvents.Count}({string.Join(", ", createdEvents)}) auto events were successfully created.", true);
}
_createEventsCoroutine = null;
}
#endregion Monument Event
#region Chat/Console Command Handler
private object OnPlayerCommand(BasePlayer player, string command, string[] args) {
return CheckCommand(player, command, true);
}
private object OnServerCommand(ConsoleSystem.Arg arg) {
return CheckCommand(arg?.Player(), arg?.cmd?.FullName, false);
}
private object CheckCommand(BasePlayer player, string command, bool isChat) {
if (player == null || string.IsNullOrEmpty(command)) {
return null;
}
command = command.ToLower().TrimStart('/');
if (string.IsNullOrEmpty(command)) {
return null;
}
LeftZone leftZone;
if (_pvpDelays.TryGetValue(player.userID, out leftZone)) {
var baseEvent = GetBaseEvent(leftZone.eventName);
if (baseEvent != null && baseEvent.CommandWorksForPVPDelay) {
if (IsBlockedCommand(baseEvent, command, isChat)) {
return _false;
}
}
}
var result = GetPlayerZoneIds(player);
if (result == null || result.Length == 0 || result.Length == 1 && string.IsNullOrEmpty(result[0])) {
return null;
}
foreach (var zoneId in result) {
string eventName;
if (_activeDynamicZones.TryGetValue(zoneId, out eventName)) {
PrintDebug($"Checking command: {command} , zoneId: {zoneId}");
var baseEvent = GetBaseEvent(eventName);
if (baseEvent != null) {
if (IsBlockedCommand(baseEvent, command, isChat)) {
return _false;
}
}
}
}
return null;
}
private bool IsBlockedCommand(BaseEvent baseEvent, string command, bool isChat) {
if (baseEvent != null && baseEvent.CommandList.Count > 0) {
var commandExist = baseEvent.CommandList.Any(entry => isChat ? entry.StartsWith("/") && entry.Substring(1).Equals(command) : !entry.StartsWith("/") && command.Contains(entry));
if (baseEvent.UseBlacklistCommands) {
if (commandExist) {
PrintDebug($"Use blacklist, Blocked command: {command}", true);
return true;
}
}
else {
if (!commandExist) {
PrintDebug($"Use whitelist, Blocked command: {command}", true);
return true;
}
}
}
return false;
}
#endregion Chat/Console Command Handler
#endregion Events
#region DynamicZone Handler
private void HandleParentedEntityEvent(string eventName, BaseEntity parentEntity, bool delay = true) {
if (parentEntity == null || parentEntity.net == null) {
return;
}
var baseEvent = GetBaseEvent(eventName);
if (baseEvent == null) {
return;
}
if (delay && baseEvent.EventStartDelay > 0f) {
timer.Once(baseEvent.EventStartDelay, () => HandleParentedEntityEvent(eventName, parentEntity, false));
return;
}
PrintDebug($"Trying to create parented entity event {eventName} on entity({parentEntity}).");
var zoneId = parentEntity.net.ID.ToString();
var parentZone = baseEvent.GetDynamicZone() as IParentZone;
if (CreateDynamicZone(eventName, parentEntity.transform.position, zoneId, delay : false)) {
timer.Once(0.25f, () => {
var zone = GetZoneById(zoneId);
PrintDebug($"Gets the zone({zoneId} | {zone}) created by the {eventName} event.", true);
if (parentEntity != null && zone != null) {
var zoneTransform = zone.transform;
zoneTransform.SetParent(parentEntity.transform);
zoneTransform.rotation = parentEntity.transform.rotation;
zoneTransform.position = parentZone != null ? parentEntity.transform.TransformPoint(parentZone.Center) : parentEntity.transform.position;
PrintDebug($"The zone({zoneId} | {eventName}) was parented to entity({parentEntity}).", true);
}
else {
PrintDebug($"ERROR: The zone({zoneId} | {zone} | {parentEntity}) created by the {eventName} event is empty.", error : true);
DeleteDynamicZone(zoneId);
}
}
);
}
}
private bool HandleMonumentEvent(string eventName, Transform transform, MonumentEvent monumentEvent) {
var position = monumentEvent.TransformPosition != Vector3.zero ? transform.TransformPoint(monumentEvent.TransformPosition) : transform.position;
return CreateDynamicZone(eventName, position, monumentEvent.ZoneId, monumentEvent.GetDynamicZone().ZoneSettings(transform));
}
private bool HandleGeneralEvent(GeneralEventType generalEventType, BaseEntity baseEntity, bool useEntityId) {
var eventName = generalEventType.ToString();
if (useEntityId && _activeDynamicZones.ContainsKey(baseEntity.net.ID.ToString())) {
PrintDebug($"The event({eventName} | {baseEntity.net.ID}) created by entity({baseEntity}) already exists");
return false;
}
if (!CanCreateDynamicPVP(eventName, baseEntity)) {
return false;
}
var baseEvent = GetBaseEvent(eventName);
if (baseEvent == null) {
return false;
}
var position = baseEntity.transform.position;
position.y = TerrainMeta.HeightMap.GetHeight(position);
return CreateDynamicZone(eventName, position, useEntityId ? baseEntity.net.ID.ToString() : null, baseEvent.GetDynamicZone().ZoneSettings(baseEntity.transform));
}
private bool CreateDynamicZone(string eventName, Vector3 position, string zoneId = "", string[] zoneSettings = null, bool delay = true) {
if (position == Vector3.zero) {
PrintDebug($"ERROR: Invalid location, zone({eventName}) creation failed.Skipping event creation.", error : true);
return false;
}
var baseEvent = GetBaseEvent(eventName);
if (baseEvent == null) {
return false;
}
if (delay && baseEvent.EventStartDelay > 0f) {
timer.Once(baseEvent.EventStartDelay, () => CreateDynamicZone(eventName, position, zoneId, zoneSettings, false));
return false;
}
float duration = -1;
var timedEvent = baseEvent as ITimedEvent;
if (timedEvent != null) {
var timedDisable = baseEvent as ITimedDisable;
if (timedDisable == null || !timedDisable.IsTimedDisabled()) {
duration = timedEvent.Duration;
}
}
if (string.IsNullOrEmpty(zoneId)) {
zoneId = DateTime.Now.ToString("HHmmssffff");
}
var dynamicZone = baseEvent.GetDynamicZone();
zoneSettings = zoneSettings ?? dynamicZone.ZoneSettings();
PrintDebug($"Trying create zone: {eventName}({zoneId} | {position}) {(dynamicZone is ISphereZone ? $"(Radius : {
(dynamicZone as ISphereZone).Radius
}
m)" : $"(Size : {
(dynamicZone as ICubeZone)?.Size
}
)")} {(dynamicZone is IParentZone ? $"(Center : {
(dynamicZone as IParentZone).Center
}
) " : null)}(Duration: {duration}s).");
var zoneAdded = CreateZone(zoneId, zoneSettings, position);
if (zoneAdded) {
if (!_activeDynamicZones.ContainsKey(zoneId)) {
_activeDynamicZones.Add(zoneId, eventName);
CheckHooks();
}
var stringBuilder = Pool.Get<StringBuilder>();
stringBuilder.Clear();
var domeEvent = baseEvent as IDomeEvent;
var sphereZone = dynamicZone as ISphereZone;
if (DomeCreateAllowed(domeEvent, sphereZone)) {
var domeAdded = CreateDome(zoneId, position, sphereZone.Radius, domeEvent.DomesDarkness);
if (!domeAdded) {
PrintDebug($"ERROR: Dome NOT added for zone({zoneId}).", error : true);
}
else {
stringBuilder.Append("Dome,");
}
}
var botEvent = baseEvent as IBotEvent;
if (BotReSpawnAllowed(botEvent)) {
var botsSpawned = SpawnBots(position, botEvent.BotProfileName, zoneId);
if (!botsSpawned) {
PrintDebug($"ERROR: Bot NOT spawned for zone({zoneId}).", error : true);
}
else {
stringBuilder.Append("Bots,");
}
}
var mappingAdded = TryCreateMapping(zoneId, baseEvent.Mapping);
if (!mappingAdded) {
PrintDebug($"ERROR: Mapping Not added for zone({zoneId}).", error : true);
}
else {
stringBuilder.Append("Mapping,");
}
PrintDebug($"Created zone({zoneId} | {eventName}) ({stringBuilder.ToString().TrimEnd(',')}).", true);
HandleDeleteDynamicZone(zoneId, duration, eventName);
stringBuilder.Clear();
Pool.Free(ref stringBuilder);
return true;
}
PrintDebug($"ERROR: Create zone({eventName}) failed.", error : true);
return false;
}
private void HandleDeleteDynamicZone(string zoneId, float duration, string eventName) {
if (duration > 0f) {
TryRemoveEventTimer(zoneId);
PrintDebug($"The zone({zoneId} | {eventName}) will be deleted after {duration} seconds.", true);
_eventTimers.Add(zoneId, timer.Once(duration, () => HandleDeleteDynamicZone(zoneId)));
}
}
private void HandleDeleteDynamicZone(string zoneId) {
string eventName;
if (string.IsNullOrEmpty(zoneId) || !_activeDynamicZones.TryGetValue(zoneId, out eventName)) {
PrintDebug($"ERROR: Invalid zoneID: {zoneId}.", error : true);
return;
}
if (Interface.CallHook("OnDeleteDynamicPVP", zoneId, eventName) != null) {
return;
}
var baseEvent = GetBaseEvent(eventName);
if (baseEvent == null) {
return;
}
if (baseEvent.EventStopDelay > 0f) {
TryRemoveEventTimer(zoneId);
if (baseEvent.GetDynamicZone() is IParentZone) {
var zone = GetZoneById(zoneId);
if (zone != null) {
zone.transform.SetParent(null, true);
}
}
_eventTimers.Add(zoneId, timer.Once(baseEvent.EventStopDelay, () => DeleteDynamicZone(zoneId)));
}
else {
DeleteDynamicZone(zoneId);
}
}
private bool DeleteDynamicZone(string zoneId) {
string eventName;
if (string.IsNullOrEmpty(zoneId) || !_activeDynamicZones.TryGetValue(zoneId, out eventName)) {
PrintDebug($"ERROR: Invalid zoneID: {zoneId}.", error : true);
return false;
}
TryRemoveEventTimer(zoneId);
var baseEvent = GetBaseEvent(eventName);
if (baseEvent == null) {
return false;
}
var stringBuilder = Pool.Get<StringBuilder>();
stringBuilder.Clear();
var dynamicZone = baseEvent.GetDynamicZone();
if (DomeCreateAllowed(baseEvent as IDomeEvent, dynamicZone as ISphereZone)) {
var domeRemoved = RemoveDome(zoneId);
if (!domeRemoved) {
PrintDebug($"ERROR: Dome NOT removed for zone({zoneId} | {eventName}).", error : true);
}
else {
stringBuilder.Append("Dome,");
}
}
if (BotReSpawnAllowed(baseEvent as IBotEvent)) {
var botsRemoved = KillBots(zoneId);
if (!botsRemoved) {
PrintDebug($"ERROR: Bot NOT killed for zone({zoneId} | {eventName}).", error : true);
}
else {
stringBuilder.Append("Bots,");
}
}
var mappingRemoved = TryRemoveMapping(zoneId);
if (!mappingRemoved) {
PrintDebug($"ERROR: Mapping NOT removed for zone({zoneId} | {eventName}).", error : true);
}
else {
stringBuilder.Append("Mapping,");
}
var players = GetPlayersInZone(zoneId);
var zoneRemoved = RemoveZone(zoneId, eventName);
if (zoneRemoved) {
// Release zone players immediately
PrintDebug($"Release zone players : {string.Join(", ", players.Select(x => x.displayName))}");
foreach (var player in players) {
OnExitZone(zoneId, player);
}
if (_activeDynamicZones.Remove(zoneId)) {
CheckHooks();
}
PrintDebug($"Deleted zone({zoneId} | {eventName}) ({stringBuilder.ToString().TrimEnd(',')}).", true);
}
else {
PrintDebug($"ERROR: Delete zone({zoneId} | {eventName} | {stringBuilder.ToString().TrimEnd(',')}) failed.", error : true);
}
stringBuilder.Clear();
Pool.Free(ref stringBuilder);
return zoneRemoved;
}
private void DeleteOldDynamicZones() {
var zoneIds = GetZoneIds();
if (zoneIds == null || zoneIds.Length <= 0) {
return;
}
int attempts = 0, successes = 0;
foreach (var zoneId in zoneIds) {
var zoneName = GetZoneName(zoneId);
if (zoneName == ZoneName) {
attempts++;
var zoneRemoved = RemoveZone(zoneId);
if (zoneRemoved) {
successes++;
}
TryRemoveMapping(zoneId);
}
}
// ... [Code preview truncated due to length. Full source in attached file] ...