C#:
/*
* <----- End-User License Agreement ----->
*
* You may not merge, publish, distribute, sublicense, or sell copies of This Software without the Developer’s consent.Copy or modify is allowed for personal use only.*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*
* Developer: nivex (mswenson82@yahoo.com)
*
* Copyright © nivex.All rights reserved.███▄ ██ ██▓ ██▒ ██▒▓█████ ██▓ ██▓
██ ▀█ ░█ ▓██▒▓██░ ██▒▓█ ▀ ▓██▒██▓
▓██ ▀█ ██▒▒██▒ ▓██ █▒░▒████ ▒██▒
▓██▒ ▐▌██▒░██░ ▒██ █░░▒▓█ ▄ ░██░██░
▒██░ ▓██░░██░ ▒▀█░ ░▒████▒░██░ ██▓
░ ▒░ ▒ ▒ ░▓ ░ ▐░ ░░ ▒░ ░░▓ ▓ ░
░ ░░ ░ ▒░ ▒ ░ ░ ░░ ░ ░ ░ ▒ ░ ▒ ░
░ ░ ░ ▒ ░ ░░ ░ ▒ ░ ▒
░ ░ ░ ░ ░ ░ ░
*/
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using static Oxide.Plugins.AbandonedBasesExtensionMethods.ExtensionMethods;
namespace Oxide.Plugins {
[Info("Abandoned Bases", "nivex", "2.1.9")]
[Description("Allows bases to become raidable when the owner becomes inactive")]
public class AbandonedBases : RustPlugin {
#region Variables
[PluginReference]
Plugin Backpacks, Economics, ServerRewards, IQEconomic, RaidableBases, Clans, Friends, Notify, AdvancedAlerts, ZoneManager, SkillTree, LimitEntities, XLevels;
private new const string Name = "Abandoned Bases";
private List<string> ID_FLOORS = new() {
"floor", "floor.frame", "floor.grill", "floor.ladder.hatch", "floor.triangle", "floor.triangle.frame", "floor.triangle.grill", "floor.triangle.ladder.hatch"
}
;
private List<string> ID_DQD = new() {
"foundation", "foundation.triangle", "roof.triangle", "roof"
}
;
private List<string> TrueDamage = new() {
"Barricade", "SimpleBuildingBlock", "IceFence", "TeslaCoil", "BaseTrap", "GunTrap", "FlameTurret", "FogMachine", "SamSite", "AutoTurret"
}
;
private List<string> ExcludedMounts = new() {
"beachchair", "boogieboard", "cardtable", "chair", "chippyarcademachine", "computerstation", "drumkit", "microphonestand", "piano", "secretlabchair", "slotmachine", "sofa", "xylophone"
}
;
private Coroutine reportCoroutine;
private bool isLoaded {
get;
set;
}
private bool DebugMode {
get;
set;
}
private bool newSave {
get;
set;
}
private bool IsPurgeEnabled {
get;
set;
}
private StoredData data {
get;
set;
}
= new();
private StringBuilder _sb {
get;
set;
}
= new();
private Coroutine abandonedCoroutine {
get;
set;
}
private List<string> _waitingList {
get;
set;
}
= new();
private List<UserConversion> _conversions {
get;
set;
}
= new();
private List<AbandonedBuilding> AbandonedBuildings {
get;
set;
}
= new();
private Dictionary<ulong, DelaySettings> PvpDelay {
get;
set;
}
= new();
private Dictionary<ulong, List<Notification>> _notifications {
get;
set;
}
= new();
private Dictionary<ulong, AbandonedBuilding> AbandonedReferences {
get;
set;
}
= new();
public enum SphereColor {
None, Blue, Cyan, Green, Magenta, Purple, Red, Yellow
}
public class UserConversion {
public string userid;
public Coroutine co;
public HashSet<ulong> owners;
public UserConversion(string userid, HashSet<ulong> owners, Coroutine co) {
this.owners = owners;
this.userid = userid;
this.co = co;
}
public bool Exists(IPlayer user, HashSet<ulong> owners) {
if (co == null) {
return false;
}
return userid == user.Id || this.owners.Overlaps(owners);
}
}
public class Notification {
public BasePlayer player;
public string messageEx;
}
private class StoredData {
[JsonProperty("Last Seen", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, int> LastSeen {
get;
set;
}
= new();
[JsonProperty("Cooldown Between Conversion", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenConversion {
get;
set;
}
= new();
[JsonProperty("Cooldown Between Cancel", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenCancel {
get;
set;
}
= new();
[JsonProperty("Cooldown Between Events", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenEvents {
get;
set;
}
= new();
[JsonProperty("Activities", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ActivityInfo> Activity {
get;
set;
}
= new();
public DateTime LastRunTime {
get;
set;
}
= DateTime.MinValue;
public int protocol;
public StoredData() {
}
}
public class Payment {
public Payment(BasePlayer player, double cost = 0, List<CustomCostOptions> options = null) {
userId = player.userID;
this.options = options;
this.player = player;
this.cost = cost;
}
public double cost {
get;
set;
}
public ulong userId {
get;
set;
}
public List<CustomCostOptions> options {
get;
set;
}
public BasePlayer player {
get;
set;
}
}
internal class ActivityInfo {
[JsonProperty(PropertyName = "owners", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public HashSet<ulong> owners {
get;
set;
}
= new();
[JsonProperty(PropertyName = "permission")]
public string perm {
get;
set;
}
= string.Empty;
[JsonProperty(PropertyName = "total")]
public int total {
get;
set;
}
internal ActivityInfo() {
}
public bool SameOwners(HashSet<ulong> owners) => owners.All(this.owners.Contains);
internal int GetLimit(Configuration config) {
if (string.IsNullOrEmpty(perm) || !perm.PermissionExists() || !owners.Exists(owner => owner.HasPermission(perm))) {
CheckPermission(config);
}
foreach (var purge in config.Purges) {
if (perm == purge.Permission) {
return purge.NoPurge ? 0 : purge.Limit;
}
}
return 0;
}
internal void CheckPermission(Configuration config) {
var limit = int.MinValue;
foreach (var owner in this.owners) {
var purge = PurgeSettings.Find(config, owner);
if (purge == null || string.IsNullOrEmpty(purge.Permission)) {
continue;
}
if (purge.Limit > limit) {
perm = purge.Permission;
limit = purge.Limit;
}
if (purge.Limit <= 0) {
perm = purge.Permission;
break;
}
}
}
}
public class DelaySettings {
internal AbandonedBuilding Building {
get;
set;
}
public Timer Timer {
get;
set;
}
public float time {
get;
set;
}
public bool valid => Building != null && !Building.isDestroyed;
public void Destroy() {
if (Timer != null && !Timer.Destroyed) {
Timer.Callback();
Timer.Destroy();
}
}
}
internal class AbandonedBuilding : FacepunchBehaviour {
public bool Equals(AbandonedBuilding other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return _guid.Equals(other._guid);
}
public override bool Equals(object obj) {
return obj is AbandonedBuilding other && Equals(other);
}
public override int GetHashCode() {
return _guid.GetHashCode();
}
public static bool operator ==(AbandonedBuilding left, AbandonedBuilding right) {
if (ReferenceEquals(left, null)) return ReferenceEquals(right, null);
return left.Equals(right);
}
public static bool operator !=(AbandonedBuilding left, AbandonedBuilding right) {
return !(left == right);
}
internal class EntityOwner {
public BaseEntity Entity;
public ulong OwnerID;
public EntityOwner(BaseEntity entity) {
Entity = entity;
OwnerID = entity.OwnerID;
}
}
internal class Raider {
public ulong userid;
public string username;
public bool IsParticipant;
public bool HasEntered;
public bool IsAdmin;
public float activeDuration;
public float lastActiveTime;
public Vector3 lastPosition;
public BasePlayer _player;
public BasePlayer player {
get {
if (_player == null) {
_player = RustCore.FindPlayerById(userid);
}
return _player;
}
}
public Raider(BasePlayer target, float duration) {
activeDuration = duration * 60f;
_player = target;
IsAdmin = target.IsAdmin;
userid = target.userID;
username = target.displayName;
}
public float PlayerActivityTimeLeft() {
if (activeDuration <= 0f) {
return float.PositiveInfinity;
}
return activeDuration - (Time.time - lastActiveTime);
}
}
internal List<ulong> IsAllowed {
get;
set;
}
= new();
internal List<ulong> Messages {
get;
set;
}
= new();
internal HashSet<uint> buildingIDs {
get;
set;
}
= new();
internal List<BuildingPrivlidge> privs {
get;
set;
}
= new();
internal HashSet<ulong> owners {
get;
set;
}
= new();
internal HashSet<ulong> others {
get;
set;
}
= new();
internal List<BaseEntity> entities {
get;
set;
}
= new();
internal List<SphereEntity> spheres {
get;
set;
}
= new();
internal List<Vector3> compound {
get;
set;
}
= new();
internal List<Vector3> foundations {
get;
set;
}
= new();
internal Dictionary<ulong, BasePlayer> intruders {
get;
set;
}
= new();
internal Dictionary<ulong, Raider> raiders {
get;
set;
}
= new();
internal List<StorageContainer> containers {
get;
set;
}
= new();
internal Dictionary<ulong, EntityOwner> EntityOwners {
get;
set;
}
= new();
internal MapMarkerGenericRadius genericMarker = null;
internal VendingMachineMapMarker vendingMarker = null;
internal bool IsOwnerLocked {
get;
set;
}
internal bool IsDamaged {
get;
set;
}
internal bool DisableEventCooldown {
get;
set;
}
internal PluginTimers timer {
get;
set;
}
internal BaseCombatEntity anchor {
get;
set;
}
internal SimplePrivilege simplePriv {
get;
set;
}
internal DateTime DespawnDateTime {
get;
set;
}
internal float radius {
get;
set;
}
internal bool AutomatedEvent {
get;
set;
}
internal bool markerCreated {
get;
set;
}
internal bool isDestroyed {
get;
set;
}
internal bool IsClaimed {
get;
set;
}
internal bool CanShowDiscordEnd {
get;
set;
}
= true;
internal bool AllowPVP {
get;
set;
}
internal bool IsExpired {
get;
set;
}
internal SphereCollider _collider {
get;
set;
}
internal Payment payment {
get;
set;
}
internal List<string> groups {
get;
set;
}
= new();
internal GameObject go {
get;
set;
}
internal float cancelCooldownTime {
get;
set;
}
internal bool isCanceled {
get;
set;
}
internal string raiderName {
get;
set;
}
internal ulong raiderId {
get;
set;
}
internal string currentName {
get;
set;
}
internal ulong currentId {
get;
set;
}
internal string previousName {
get;
set;
}
internal ulong previousId {
get;
set;
}
internal bool canReassign {
get;
set;
}
= true;
internal ActivityInfo activity {
get;
set;
}
internal int lootAmount {
get;
set;
}
internal Coroutine _coroutine {
get;
set;
}
internal AbandonedBases Instance {
get;
set;
}
internal bool LockBaseToFirstAttacker => AllowPVP ? config.Abandoned.LockBaseToFirstAttackerPVP : config.Abandoned.LockBaseToFirstAttackerPVE;
internal bool EjectFromLockedBase;
internal string GetGrid() => PositionToGrid(center);
internal Vector3 _center {
get;
set;
}
public EventType eventType;
private Configuration config => Instance.config;
private StoredData data => Instance.data;
public bool HasEventCooldown(BasePlayer player) => Instance.HasEventCooldown(player, this);
public bool HasCooldown(BasePlayer player, string perm) => Instance.HasCooldown(player, player.userID, perm, data.CooldownBetweenEvents);
public void Message(BasePlayer player, string key, params object[] args) => Instance.Message(player, key, args);
public void LogToFile(string filename, string text) => Instance.LogToFile(filename, text, Instance, false, true);
internal Vector3 center {
get {
if (!anchor.IsKilled()) {
_center = anchor.transform.position;
}
return _center;
}
set {
_center = value;
}
}
public bool IsAbandonedSleeper(BasePlayer victim) => config.PlayersCanKillAbandonedSleepers && AutomatedEvent && !victim.IsConnected && owners.Contains(victim.userID);
public bool InRange2D(Vector3 from, float x = 0f) => AbandonedBases.InRange2D(from, center, radius + x);
public bool InRange3D(Vector3 from, float x = 0f) => AbandonedBases.InRange3D(from, center, radius + x);
public bool NearCompound(Vector3 from) => compound.Exists(to => AbandonedBases.InRange3D(from, to, 3f));
public bool NearFoundation(Vector3 from, float radius = 3f) => foundations.Exists(to => AbandonedBases.InRange3D(from, to, radius));
public bool NearFoundation(BasePlayer player, float radius = 3f) {
if (player.HasParent() && player.GetParentEntity() is Tugboat tugboat) {
return Instance.GetVehiclePrivilege(tugboat.children) == simplePriv;
}
return NearFoundation(player.transform.position, radius);
}
public Raider GetRaider(BasePlayer player) {
if (!raiders.TryGetValue(player.userID, out var ri)) {
raiders[player.userID] = ri = new(player, InactivePlayerActivityTime);
}
return ri;
}
public List<Raider> GetRaiders() => raiders.Values.ToList();
public List<BasePlayer> GetIntruders() => intruders.Values.Where(x => !x.IsKilled());
public List<ulong> GetIntruderIds() => GetIntruders().Select(x => (ulong)x.userID).ToList();
public List<ulong> GetParticipantIds() => GetEligibleParticipants().Select(x => x.userid).ToList();
public List<Raider> GetEligibleParticipants() => GetRaiders().Where(raider => raider switch {
{
IsParticipant : false
}
=> false, {
IsAdmin : true
}
when config.Abandoned.RemoveAdminRaiders => false, {
player : null
}
=> true, {
player : {
IsFlying : true, limitNetworking : true
}
}
=> false, _ => true
}
);
public bool IsParticipant(ulong userid) => raiders.TryGetValue(userid, out var raider) && raider.IsParticipant;
public void AddEntity(BaseEntity entity) {
Instance.AbandonedReferences[entity.net.ID.Value] = this;
if (owners.Contains(entity.OwnerID)) {
entities.Add(entity);
}
}
public static bool AddRange(AbandonedBases m, List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> foundations, List<Vector3> floors, List<Vector3> compound, List<Vector3> walls, IPlayer user, ulong userid, bool isAutomated) {
foreach (var e in entities.ToList()) {
if (e.IsKilled()) {
entities.Remove(e);
continue;
}
if (e is BuildingBlock) {
compound.Add(e.transform.position);
}
if (e.ShortPrefabName == "foundation" || e.ShortPrefabName == "foundation.triangle") {
if (m.config.Abandoned.Twig || e is BuildingBlock block && block.grade != BuildingGrade.Enum.Twigs) {
foundations.Add(e.transform.position);
}
}
else if (e.skinID == 1337424001 && e is CollectibleEntity) {
compound.Add(e.transform.position);
foundations.Add(e.transform.position);
}
else if (e.ShortPrefabName.Contains("external.high")) {
compound.Add(e.transform.position);
}
else if (e.ShortPrefabName == "wall" || e.ShortPrefabName == "wall.half" || e.ShortPrefabName == "wall.window") {
if (m.config.Abandoned.Twig || e is BuildingBlock block && block.grade != BuildingGrade.Enum.Twigs) {
walls.Add(e.transform.position);
}
}
else if (m.ID_FLOORS.Contains(e.ShortPrefabName)) {
floors.Add(e.transform.position);
}
}
if (foundations.Count < m.config.Abandoned.FoundationLimit || walls.Count < m.config.Abandoned.WallLimit) {
return m.IsPurgeEnabled && m.config.Abandoned.GetDespawnSeconds(isAutomated) <= 0f && user == null && !m.CanPurge(userid, Epoch.Current);
// allow when auto converting bases that are built during purge
}
AddContainers(entities, containers, foundations);
return true;
}
public static bool AddRange(List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> compound, int minLoot, ref int loot) {
List<BaseEntity.Slot> _checkSlots = new() {
BaseEntity.Slot.Lock, BaseEntity.Slot.UpperModifier, BaseEntity.Slot.MiddleModifier, BaseEntity.Slot.LowerModifier
}
;
foreach (var e in entities.ToList()) {
if (e.IsKilled()) {
entities.Remove(e);
continue;
}
foreach (var checkSlot in _checkSlots) {
var slot = e.GetSlot(checkSlot);
if (slot == null) continue;
if (entities.Contains(slot)) continue;
entities.Add(slot);
}
var container = e as IItemContainerEntity;
if (container?.inventory?.itemList != null) {
loot += container.inventory.itemList.Count;
}
compound.Add(e.transform.position);
}
AddContainers(entities, containers, compound);
return minLoot <= 0 || loot >= minLoot;
}
private static void AddContainers(List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> foundations) {
if (foundations.Count > 0) {
Vector3 lowestFoundation = foundations[0];
foreach (var foundation in foundations) {
if (foundation.y < lowestFoundation.y) {
lowestFoundation = foundation;
}
}
foreach (var e in entities) {
if (!(e is StorageContainer container)) {
continue;
}
if (!IsBox(e) && !(e is BuildingPrivlidge)) {
continue;
}
if (e == null || e.IsDestroyed || !e.isSpawned || e.transform.position.y + 3f < lowestFoundation.y) {
continue;
}
containers.Add(container);
}
}
}
public void TrySetOwner(IPlayer user) {
if (user == null || user.IsServer) {
return;
}
var player = user.Object as BasePlayer;
if (player == null || player.IsFlying || player.limitNetworking) {
return;
}
GetRaider(player).IsParticipant = true;
SetOwner(player.userID, player.displayName, player.userID, player.displayName);
}
public void ResetOwner() {
raiders.Remove(previousId);
IsOwnerLocked = false;
canReassign = true;
raiderId = 0uL;
raiderName = null;
SetOwner(previousId, previousName, previousId, previousName);
UpdateMarkers();
}
public void TrySetOwnerLock(BasePlayer attacker, bool canEarnParticipation) {
if (IsOwnerLocked && !CanBypass(attacker) && !IsAlly(attacker)) {
TryEjectFromLockedBase(attacker);
return;
}
if (IsEventCompleted && IsParticipant(attacker.userID)) {
return;
}
if (!attacker.IsHuman() || HasEventCooldown(attacker)) {
return;
}
if (canEarnParticipation) {
GetRaider(attacker).IsParticipant = true;
}
if (owners.Contains(attacker.userID) || IsAlly(attacker)) {
return;
}
if (string.IsNullOrEmpty(raiderName)) {
raiderName = attacker.displayName;
raiderId = attacker.userID;
GetRaider(attacker).IsParticipant = true;
}
if (canReassign && LockBaseToFirstAttacker) {
IsOwnerLocked = true;
canReassign = false;
SetOwner(attacker.userID, attacker.displayName, currentId, currentName);
GetRaider(attacker).IsParticipant = true;
}
Invoke(UpdateMarkers, 0f);
}
public static bool CanBypass(BasePlayer player) {
return !player.IsHuman() || player.IsFlying || player.limitNetworking || player.HasPermission("abandonedbases.canbypass");
}
public bool CanDamageBuilding(BasePlayer player) {
return owners.Contains(player.userID) || IsEventCompleted && IsParticipant(player.userID);
}
public bool IsOwner(BasePlayer player) {
return owners.Contains(player.userID) || CanBypass(player) || IsAlly(player);
}
public void SetOwner(ulong newid, string newname, ulong currid, string currname) {
previousName = currname;
previousId = currid;
currentName = newname;
currentId = newid;
}
public bool EnforceCooldownBetweenEvents(BasePlayer player) {
return !IsParticipant(player.userID) && Instance.EnforceCooldownBetweenEvents(player);
}
public void TryEjectFromLockedBase(BasePlayer player) {
if (!EjectFromLockedBase || !IsOwnerLocked || owners.Contains(player.userID) && !EnforceCooldownBetweenEvents(player)) {
return;
}
if (anchor is not Tugboat && !NearFoundation(player.transform.position)) {
return;
}
if (anchor is Tugboat && !(player.GetParentEntity() is Tugboat)) {
return;
}
RemovePlayer(player);
}
public void RemovePlayer(BasePlayer player) {
var m = player.GetMounted();
if (m != null) {
m.DismountPlayer(player, true);
}
if (player.HasParent()) {
player.SetParent(null);
}
var position = GetEjectLocation(player.transform.position, 10f, center, radius);
player.Teleport(player.IsFlying ? position.WithY(player.transform.position.y) : position);
player.SendNetworkUpdateImmediate();
intruders.Remove(player.userID);
}
public static Vector3 GetEjectLocation(Vector3 a, float distance, Vector3 target, float radius) {
var position = ((a.XZ3D() - target.XZ3D()).normalized * (radius + distance)) + target;
// credits ZoneManager
float y = TerrainMeta.HighestPoint.y + 250f;
if (Physics.Raycast(position + new Vector3(0f, y), Vector3.down, out var hit, Mathf.Infinity, 10551313, QueryTriggerInteraction.Ignore)) {
position.y = hit.point.y + 0.75f;
}
else position.y = Mathf.Max(TerrainMeta.HeightMap.GetHeight(position), TerrainMeta.WaterMap.GetHeight(position)) + 0.75f;
return position;
}
public void TryMessage(BasePlayer player, string key, params object[] args) {
if (Messages.Contains(player.userID)) return;
ulong userid = player.userID;
Messages.Add(userid);
timer.Once(10f, () => Messages.Remove(userid));
Message(player, key, args);
}
private readonly Guid _guid;
public AbandonedBuilding() {
_guid = Guid.NewGuid();
}
public void Setup(AbandonedBases m, IPlayer user, PluginTimers timer, List<Vector3> compound, List<Vector3> foundations, List<BaseEntity> entities, List<StorageContainer> containers, HashSet<ulong> owners, HashSet<ulong> others, Vector3 center, Payment payment, float radius, bool allowPVP, bool isAutomated) {
Instance = m;
TrySetOwner(user);
AutomatedEvent = isAutomated;
markerCreated = user == null && !config.Abandoned.AutoMarkers || user != null && !config.Abandoned.ManualMarkers;
canReassign = currentId == 0 || string.IsNullOrEmpty(currentName);
EjectFromLockedBase = allowPVP ? config.Abandoned.EjectLockedPVP : config.Abandoned.EjectLockedPVE;
this.Instance.AbandonedBuildings.Add(this);
this.AllowPVP = allowPVP;
this.compound = compound;
this.foundations = foundations;
this.containers = containers;
this.entities = entities;
this.payment = payment;
this.owners = owners;
this.others = others;
this.radius = radius;
this.timer = timer;
this.center = center;
others.RemoveWhere(owners.Contains);
Interface.Oxide.NextTick(() => {
if (m.CanDebug(user)) {
user.Message($"Conversion finalizing event...");
}
TryInvokeMethod(SetupEntities);
TryInvokeMethod(SetupCollider);
TryInvokeMethod(CompleteConvertPayment);
TryInvokeMethod(Announce);
TryInvokeMethod(InvokeDespawnInactive);
TryInvokeMethod(SpawnNpcs);
TryInvokeMethod(SetupSleepers);
UpdateActivity(true);
if (m.CanDebug(user)) {
user.Message($"Conversion setup complete.Event has started");
}
cancelCooldownTime = Time.time + config.Abandoned.CancelCooldown;
}
);
Interface.CallHook("OnAbandonedBaseStarted", center, radius, AllowPVP, GetIntruders(), GetIntruderIds(), entities);
}
private void Awake() {
go = gameObject;
}
private float protectorDeltaTime;
private void Update() {
if (EjectFromLockedBase) {
protectorDeltaTime += Time.deltaTime;
if (protectorDeltaTime >= 1f) {
protectorDeltaTime = -300f;
Protector();
protectorDeltaTime = 0f;
}
}
}
private void OnDestroy() {
if (groups.Count > 0) {
Plugin BotReSpawn = Interface.Oxide.RootPluginManager.GetPlugin("BotReSpawn");
groups.ForEach(group => BotReSpawn?.Call("RemoveGroupSpawn", group));
}
if (!isDestroyed) {
DestroyMe();
}
}
private bool IsAlly(BasePlayer player) => Instance.IsAlly(player.userID, currentId);
private void Protector() {
if (isDestroyed) {
return;
}
if (!IsPlayerActive(currentId)) {
ResetOwner();
}
if (intruders.Count == 0) {
return;
}
using var players = intruders.ToPooledList();
foreach (var (userid, intruder) in players) {
if (intruder.IsKilled()) {
intruders.Remove(userid);
continue;
}
if (IsAllowed.Contains(userid)) {
UpdateIntruderActivity(intruder);
continue;
}
if (!IsParticipant(userid) && EnforceCooldownBetweenEvents(intruder)) {
TryEjectFromLockedBase(intruder);
continue;
}
if (!IsOwnerLocked) {
UpdateIntruderActivity(intruder);
continue;
}
if (IsOwner(intruder)) {
UpdateIntruderActivity(intruder);
IsAllowed.Add(userid);
continue;
}
TryEjectFromLockedBase(intruder);
}
}
private void OnTriggerEnter(Collider collider) {
if (collider == null) {
return;
}
var entity = collider.ToBaseEntity();
if (entity is BasePlayer player) {
if (!intruders.ContainsKey(player.userID)) {
intruders[player.userID] = player;
var raider = GetRaider(player);
raider.HasEntered = true;
if (InactivePlayerActivityTime > 0f) {
raider.lastActiveTime = Time.time;
}
Message(player, AllowPVP ? "OnPlayerEntered" : "OnPlayerEnteredPVE");
if (config.Messages.HasEventCooldown) {
HasEventCooldown(player);
}
Interface.CallHook("OnPlayerEnteredAbandonedBase", player, center, radius, AllowPVP, GetIntruders(), GetIntruderIds(), entities);
OnPlayerEnter(player);
UpdateTime(player, true);
}
}
else if (entity is BaseMountable m) {
GetMountedPlayers(m).ForEach(player => {
if (!intruders.ContainsKey(player.userID)) {
intruders[player.userID] = player;
var raider = GetRaider(player);
raider.HasEntered = true;
if (LockBaseToFirstAttacker) {
raider.lastActiveTime = Time.time;
}
Message(player, AllowPVP ? "OnPlayerEntered" : "OnPlayerEnteredPVE");
Interface.CallHook("OnPlayerEnteredAbandonedBase", player, center, radius, AllowPVP, GetIntruders(), GetIntruderIds(), entities);
UpdateTime(player, true);
}
}
);
}
}
private void OnTriggerExit(Collider collider) {
if (collider == null) {
return;
}
var entity = collider.ToBaseEntity();
if (entity is BasePlayer player) {
OnPlayerExit(player, player.IsDead());
}
else if (entity is BaseMountable m) {
GetMountedPlayers(m).ForEach(player => OnPlayerExit(player, player.IsDead()));
}
}
public void DestroyMe() {
isDestroyed = true;
TryInvokeMethod(CancelEntitySetup);
TryInvokeMethod(KillMarkers);
TryInvokeMethod(DestroySpheres);
TryInvokeMethod(RewardPlayers);
TryInvokeMethod(PowerDownAutoTurrets);
TryInvokeMethod(RestoreEntityOwners);
TryInvokeMethod(RemoveReferences);
TryInvokeMethod(CancelInvokes);
if (eventType == EventType.Base) {
Destroy(go);
}
Destroy(this);
}
public void OnClaimed(BasePlayer player, string hook) {
GetParticipants(out var participants, out var participantIds);
Interface.CallHook(hook, player, center, radius, AllowPVP, participants, participantIds, entities);
}
private void SetupSleepers() {
if (!config.MoveInventory) {
return;
}
var boxes = containers.Where(x => !x.IsKilled() && IsBox(x) && !x.inventory.IsFull());
if (boxes.Count == 0) {
return;
}
foreach (var entity in entities) {
if (entity == null) continue;
var target = entity as BasePlayer;
if (target.IsKilled()) continue;
if (!target.IsSleeping() || target.IsConnected || target.inventory == null) continue;
using var items = target.GetPlayerItems();
while (items.Count > 0 && boxes.Count > 0) {
var box = boxes.GetRandom();
if (box.inventory.IsFull()) {
boxes.Remove(box);
continue;
}
Item item = items[0];
items.Remove(item);
if (item == null) {
continue;
}
if (config.MoveInventoryBlacklist.Contains(item.info.shortname) || !item.MoveToContainer(box.inventory)) {
item.RemoveFromContainer();
item.Remove(0f);
}
}
}
}
public void RewardPlayers() {
if (IsClaimed) {
return;
}
var raiders = GetEligibleParticipants();
foreach (var raider in raiders) {
if (!isCanceled) {
Instance.GiveRewards(raider.player, raider.userid, raiders.Count, eventType);
}
if (!DisableEventCooldown) {
SetEventCooldown(raider.userid);
}
}
if (isCanceled || Instance.IsUnloading) {
return;
}
var activeId = raiderId == 0uL ? currentId : raiderId;
var activeName = string.IsNullOrEmpty(raiderName) ? currentName : raiderName;
var players = string.Join(", ", raiders.Select(x => x.username));
string text;
if (raiders.Count == 0) text = Instance.GetMessage("Output Uncontested", null).Replace("{activeName}", activeName ?? Instance.GetUserName(activeId) ?? "UNKNOWN").Replace("{activeId}", activeId.ToString()).Replace("{center}", center.ToString()).Replace("{grid}", GetGrid()).Replace("{previousName}", previousName ?? Instance.GetUserName(previousId) ?? "UNKNOWN").Replace("{previousId}", previousId.ToString());
else text = Instance.GetMessage("Output Ended", null).Replace("{activeName}", activeName ?? Instance.GetUserName(activeId) ?? "UNKNOWN").Replace("{activeId}", activeId.ToString()).Replace("{players}", players).Replace("{center}", center.ToString()).Replace("{grid}", GetGrid()).Replace("{previousName}", previousName ?? Instance.GetUserName(previousId) ?? "UNKNOWN").Replace("{previousId}", previousId.ToString());
if (CanShowDiscordEnd && config.Discord.End) {
Instance.SendDiscordMessage(center, text);
}
if (config.UseLogFile && activeName != previousName) {
LogToFile("sar", text);
}
Puts(text);
}
private void RestoreEntityOwners() {
UpdateExpirationStatus();
if (IsExpired || IsClaimed || EntityOwners.Count == 0) {
return;
}
foreach (var (userid, eo) in EntityOwners.ToList()) {
if (eo.Entity.IsKilled()) continue;
eo.Entity.OwnerID = eo.OwnerID;
}
}
private void RemoveReferences() => Instance.RemoveReferences(this, entities);
private void CancelInvokes() {
try {
CancelInvoke(DestroyAll);
}
catch {
}
}
public void KillMarkers() {
vendingMarker.SafelyKill();
genericMarker.SafelyKill();
}
public void DestroySpheres() {
spheres.ForEach(SafelyKill);
}
public void KillCollider() {
if (_collider != null) {
DestroyImmediate(_collider);
}
}
private void CompleteConvertPayment() => CompletePayment(payment, false);
public void CompleteCancelPayment(Payment payment) => CompletePayment(payment, true);
public void SetConversionCooldown(ulong userid) {
if (!Instance.IgnoreConversionCooldowns() && config.Abandoned.CooldownBetweenConversion > 0 && !userid.HasPermission("abandonedbases.convert.nocooldown")) {
data.CooldownBetweenConversion[userid] = DateTime.Now.AddSeconds(config.Abandoned.CooldownBetweenConversion);
}
}
public void SetCancelCooldown(ulong userid) {
if (!Instance.IgnoreCancelCooldowns() && config.Abandoned.CooldownBetweenCancel > 0 && !userid.HasPermission("abandonedbases.convert.cancel.nocooldown")) {
data.CooldownBetweenCancel[userid] = DateTime.Now.AddSeconds(config.Abandoned.CooldownBetweenCancel);
}
}
public void SetEventCooldown(ulong userid) {
if (!Instance.IgnoreEventCooldowns() && config.Abandoned.CooldownBetweenEvents > 0 && !userid.HasPermission("abandonedbases.noeventcooldown")) {
data.CooldownBetweenEvents[userid] = DateTime.Now.AddSeconds(config.Abandoned.CooldownBetweenEvents);
}
}
public void SetEventCooldowns() {
if (!DisableEventCooldown) {
GetEligibleParticipants().ForEach(raider => SetEventCooldown(raider.userid));
}
}
private void CompletePayment(Payment payment, bool cancel) {
// ... [Code preview truncated due to length. Full source in attached file] ...