C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
/*
* <----- End-User License Agreement ----->
*
* You may not copy, modify, merge, publish, distribute, sublicense, or sell copies of This Software without the Developer’s consent
*
* 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 © 2022-2023 nivex
*/
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Plugins.AbandonedBasesExtensionMethods;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Abandoned Bases", "nivex", "2.1.2")]
[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;
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 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<ulong> AbandonedSleepers {
get;
set;
}
= new();
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 class UserConversion {
public string userid;
public Coroutine co;
public List<ulong> owners;
public UserConversion(string userid, List<ulong> owners, Coroutine co) {
this.owners = owners;
this.userid = userid;
this.co = co;
}
public bool Exists(IPlayer user, List<ulong> owners) {
if (co == null) {
return false;
}
return userid == user.Id || this.owners.Exists(owners.Contains);
}
}
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 string LastRunTime {
get;
set;
}
= DateTime.MinValue.ToString();
public int protocol = 243;
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 List<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(List<ulong> owners) => owners.All(this.owners.Contains);
internal int GetLimit(Configuration config) {
if (string.IsNullOrEmpty(perm) || !perm.PermissionExists() || !owners.Exists(owner => owner.HasPermission(perm))) {
SetPermission(config);
}
foreach (var purge in config.Purges) {
if (perm == purge.Permission) {
return purge.NoPurge ? 0 : purge.Limit;
}
}
return 0;
}
internal void SetPermission(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;
}
}
}
}
internal class DelaySettings {
internal AbandonedBuilding Building {
get;
set;
}
public Timer Timer {
get;
set;
}
public void Destroy() {
if (Timer != null && !Timer.Destroyed) {
Timer.Callback();
Timer.Destroy();
}
}
}
internal class AbandonedBuilding : FacepunchBehaviour {
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 Raider(BasePlayer target) {
userid = target.userID;
username = target.displayName;
}
}
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 List<ulong> owners {
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 List<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 BaseEntity anchor {
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 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() => PhoneController.PositionToGridCoord(center);
internal Vector3 _center {
get;
set;
}
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, 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 InRange(Vector3 from) => AbandonedBases.InRange(from, center, radius);
public bool NearCompound(Vector3 from) => compound.Exists(to => AbandonedBases.InRange(from, to, 3f));
public bool NearFoundation(Vector3 from) => foundations.Exists(to => AbandonedBases.InRange(from, to, 3f));
public Raider GetRaider(BasePlayer player) {
if (!raiders.TryGetValue(player.userID, out var ri)) {
raiders[player.userID] = ri = new(player);
}
return ri;
}
public static bool AddRange(AbandonedBases m, List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> foundations, List<Vector3> compound, List<Vector3> walls, IPlayer user, ulong userid) {
var floors = new List<Vector3>();
foreach (var e in entities.ToList()) {
if (e.IsKilled()) {
entities.Remove(e);
continue;
}
if (e.ShortPrefabName == "foundation" || e.ShortPrefabName == "foundation.triangle" || e.skinID == 1337424001 && e is CollectibleEntity) {
if (m.config.Abandoned.Twig || e is BuildingBlock block && block.grade != BuildingGrade.Enum.Twigs) {
foundations.Add(e.transform.position);
compound.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);
}
else if (IsBox(e) || e is BuildingPrivlidge) {
containers.Add(e as StorageContainer);
}
}
if (foundations.Count == 0 || foundations.Count < m.config.Abandoned.FoundationLimit || walls.Count < m.config.Abandoned.WallLimit) {
return m.IsPurgeEnabled && m.config.Abandoned.DespawnSecondsInactive <= 0f && user == null && !m.CanPurge(userid, Epoch.Current);
// allow when auto converting bases that are built during purge
}
return true;
}
public static bool AddRange(AbandonedBases m, List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> compound, 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;
}
if (IsBox(e) || e is BuildingPrivlidge) {
containers.Add(e as StorageContainer);
}
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);
}
return m.config.Abandoned.Tugboats.Loot <= 0 || loot >= m.config.Abandoned.Tugboats.Loot;
}
public void TrySetOwner(IPlayer user) {
if (user == null || user.IsServer) {
return;
}
var player = user.Object as BasePlayer;
if (!player || player.IsFlying || player.limitNetworking) {
return;
}
SetOwner(player.userID, player.displayName, player.userID, player.displayName);
}
public void TrySetOwnerLock(BasePlayer attacker) {
if (IsOwnerLocked && !CanBypass(attacker) && !IsAlly(attacker)) {
TryEjectFromLockedBase(attacker);
return;
}
if (!attacker.IsHuman() || HasEventCooldown(attacker)) {
return;
}
GetRaider(attacker).IsParticipant = true;
if (owners.Contains(attacker.userID) || IsAlly(attacker)) {
return;
}
if (string.IsNullOrEmpty(raiderName)) {
raiderName = attacker.displayName;
raiderId = attacker.userID;
}
if (canReassign && LockBaseToFirstAttacker) {
IsOwnerLocked = true;
canReassign = false;
SetOwner(attacker.userID, attacker.displayName, currentId, currentName);
Invoke(UpdateMarkers, 0f);
}
}
public bool CanBypass(BasePlayer player) {
return !player.IsHuman() || player.IsFlying || player.limitNetworking || player.HasPermission("abandonedbases.canbypass");
}
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 void TryEjectFromLockedBase(BasePlayer player) {
if (!EjectFromLockedBase || !IsOwnerLocked || owners.Contains(player.userID)) {
return;
}
if (anchor == null && !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);
}
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, 0f), 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);
}
public void Setup(AbandonedBases m, IPlayer user, PluginTimers timer, List<Vector3> compound, List<Vector3> foundations, List<BaseEntity> entities, List<StorageContainer> containers, List<ulong> owners, 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.radius = radius;
this.timer = timer;
this.center = center;
Interface.Oxide.NextTick(() => {
TryInvokeMethod(SetupEntities);
TryInvokeMethod(SetupCollider);
TryInvokeMethod(CompleteConvertPayment);
TryInvokeMethod(Announce);
TryInvokeMethod(InvokeDespawnInactive);
TryInvokeMethod(SpawnNpcs);
TryInvokeMethod(SetupSleepers);
UpdateActivity(owners);
cancelCooldownTime = Time.time + config.Abandoned.CancelCooldown;
}
);
Interface.CallHook("OnAbandonedBaseStarted", center, AllowPVP, intruders, entities);
}
private void Awake() {
go = gameObject;
}
private float fixedProtectorTime;
private void FixedUpdate() {
if (EjectFromLockedBase) {
fixedProtectorTime += Time.fixedDeltaTime;
if (fixedProtectorTime >= 1f) {
fixedProtectorTime = float.MinValue;
Protector();
fixedProtectorTime = 0f;
}
}
}
private void OnDestroy() {
if (groups.Count > 0) {
Plugin BotReSpawn = Interface.Oxide.RootPluginManager.GetPlugin("BotReSpawn");
groups.ForEach(group => BotReSpawn?.Call("RemoveGroupSpawn", group));
}
}
private bool IsAlly(BasePlayer player) => Instance.IsAlly(player.userID, currentId);
private void Protector() {
if (isDestroyed || !IsOwnerLocked || intruders.Count == 0) {
return;
}
foreach (var intruder in intruders.ToList()) {
if (intruder.IsKilled()) {
intruders.Remove(intruder);
continue;
}
if (IsAllowed.Contains(intruder.userID)) {
continue;
}
if (IsOwner(intruder)) {
IsAllowed.Add(intruder.userID);
continue;
}
TryEjectFromLockedBase(intruder);
}
}
private void OnTriggerEnter(Collider collider) {
if (collider == null) {
return;
}
var entity = collider.ToBaseEntity();
if (entity is BasePlayer player) {
if (!intruders.Contains(player)) {
intruders.Add(player);
GetRaider(player).HasEntered = true;
Message(player, AllowPVP ? "OnPlayerEntered" : "OnPlayerEnteredPVE");
Interface.CallHook("OnPlayerEnteredAbandonedBase", player, transform.position, AllowPVP, center, intruders, entities);
}
}
else if (entity is BaseMountable m) {
GetMountedPlayers(m).ForEach(player => {
if (!intruders.Contains(player)) {
intruders.Add(player);
GetRaider(player).HasEntered = true;
Message(player, AllowPVP ? "OnPlayerEntered" : "OnPlayerEnteredPVE");
Interface.CallHook("OnPlayerEnteredAbandonedBase", player, center, AllowPVP, intruders, entities);
}
}
);
}
}
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(KillSpheres);
TryInvokeMethod(RewardPlayers);
TryInvokeMethod(PowerDownAutoTurrets);
TryInvokeMethod(RestoreEntityOwners);
TryInvokeMethod(RemoveReferences);
TryInvokeMethod(CancelInvokes);
if (anchor.IsKilled()) Destroy(go);
Destroy(this);
}
private void SetupSleepers() {
if (!config.KillInactiveSleepers || !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.IsKilled() || entity is not BasePlayer target) continue;
if (!target.IsSleeping() || target.IsConnected || target.inventory == null) continue;
var items = target.inventory.AllItems().ToList();
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 participants = intruders.Where(IsPlayerEligible);
foreach (var player in participants) {
if (!isCanceled) {
Instance.GiveRewards(player, participants.Count);
}
if (!DisableEventCooldown) {
SetEventCooldown(player.userID);
}
}
if (isCanceled || Instance.IsUnloading) {
return;
}
var activeId = raiderId == 0uL ? currentId : raiderId;
var activeName = string.IsNullOrEmpty(raiderName) ? currentName : raiderName;
var players = string.Join(", ", participants.Select(x => x.displayName));
var text = $"{activeName ?? "UNKNOWN"} ({activeId}) has raided the base with {players} participating at {center} ({GetGrid()}) owned by {previousName ?? "UNKNOWN"} ({previousId})";
if (participants.Count == 0) text = $"{activeName ?? "UNKNOWN"} ({activeId}) base has been abandoned at {center} ({GetGrid()}) by {previousName ?? "UNKNOWN"} ({previousId})";
if (config.UseLogFile && activeName != previousName) {
LogToFile("sar", text);
}
Puts(text);
}
private bool IsPlayerEligible(BasePlayer player) {
return !(player == null || player.IsFlying || player.limitNetworking || config.Abandoned.RemoveAdminRaiders && player.IsAdmin || !GetRaider(player).IsParticipant || HasCooldown(player, "abandonedbases.noeventcooldown"));
}
private void RestoreEntityOwners() {
if (IsExpired || IsClaimed || EntityOwners.Count == 0) {
return;
}
foreach (var pair in EntityOwners.ToList()) {
if (pair.Value.Entity.IsKilled()) continue;
pair.Value.Entity.OwnerID = pair.Value.OwnerID;
}
}
private void RemoveReferences() => Instance.RemoveReferences(this, entities);
private void CancelInvokes() {
try {
CancelInvoke(DestroyAll);
}
catch {
}
}
public void KillMarkers() {
vendingMarker.SafelyKill();
genericMarker.SafelyKill();
}
public void KillSpheres() {
spheres.ToList().ForEach(sphere => sphere.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);
}
}
private void CompletePayment(Payment payment, bool cancel) {
if (cancel) {
isCanceled = config.Abandoned.Rewards.Cancel;
}
if (payment == null || payment.cost == 0 && !payment.options.IsValid()) {
return;
}
var eco = cancel ? config.Abandoned.EconomicsCancel : config.Abandoned.Economics;
if (eco > 0 && Instance.Economics.CanCall()) {
if (Convert.ToBoolean(Instance.Economics?.Call("Withdraw", payment.userId.ToString(), payment.cost))) {
if (payment.player.IsValid()) {
Message(payment.player, cancel ? "EconomicsWithdrawCancel" : "EconomicsWithdraw", payment.cost);
}
}
}
var rp = cancel ? config.Abandoned.ServerRewardsCancel : config.Abandoned.ServerRewards;
if (rp > 0 && Instance.ServerRewards.CanCall()) {
if (Convert.ToBoolean(Instance.ServerRewards?.Call("TakePoints", payment.userId, (int)payment.cost))) {
if (payment.player.IsValid()) {
Message(payment.player, cancel ? "ServerRewardPointsTakenCancel" : "ServerRewardPointsTaken", (int)payment.cost);
}
}
}
if (payment.options.IsValid()) {
TakeCustomCost(payment.player, payment.options, cancel);
}
}
private void TakeCustomCost(BasePlayer player, List<CustomCostOptions> options, bool cancel) {
var sb = new StringBuilder();
foreach (var option in options) {
if (option.Amount <= 0) continue;
var slots = player.inventory.FindItemsByItemID(option.Definition.itemid);
var amountLeft = option.Amount;
foreach (var slot in slots) {
if (slot == null || option.Skin != 0 && slot.skin != option.Skin) {
continue;
}
var taken = slot.amount > amountLeft ? slot.SplitItem(amountLeft) : slot;
if (taken == null) {
continue;
}
taken.RemoveFromContainer();
taken.Remove(0f);
amountLeft -= taken.amount;
if (amountLeft <= 0) {
string name = string.IsNullOrEmpty(option.Name) ? slot.info.displayName.english : option.Name;
sb.Append(string.Format("{0} {1}", option.Amount, name)).Append(", ");
break;
}
}
}
if (sb.Length > 2) {
sb.Length -= 2;
Message(player, cancel ? "CustomCostTakenCancel" : "CustomCostTaken", sb.ToString());
}
}
public void Announce() {
if (Instance.IsPurgeEnabled) {
return;
}
var grid = GetGrid();
foreach (var target in BasePlayer.activePlayerList) {
if (target.HasPermission("abandonedbases.notices")) {
Message(target, "Abandoned", grid);
}
}
}
public bool IsAttached(BaseEntity entity) {
if (NearCompound(entity.transform.position)) {
return true;
}
if (entity is DecayEntity decayEntity && buildingIDs.Contains(decayEntity.buildingID)) {
return true;
}
if (entity.GetBuildingPrivilege() is BuildingPrivlidge priv && buildingIDs.Contains(priv.buildingID)) {
return true;
}
return false;
}
public void RememberOwner(BaseEntity entity) {
if (entity.OwnerID == 0 || owners.Contains(entity.OwnerID)) {
Instance.AbandonedReferences[entity.net.ID.Value] = this;
EntityOwners[entity.net.ID.Value] = new(entity);
var ice = entity as IItemContainerEntity;
if (ice == null || ice.inventory == null) return;
lootAmount += ice.inventory.itemList.Count;
}
}
private void CancelEntitySetup() {
Interface.CallHook("OnAbandonedBaseEnded", center, AllowPVP, intruders, entities);
if (_coroutine != null) {
ServerMgr.Instance.StopCoroutine(_coroutine);
_coroutine = null;
}
}
private IEnumerator EntitySetup() {
float invokeTime = 0f;
int checks = 0;
foreach (var e in entities.ToList()) {
if (++checks % 100 == 0) {
yield return CoroutineEx.waitForSeconds(0.025f);
}
if (e.IsKilled() || e.net == null || e.OwnerID == 0 && e is StorageContainer || e.OwnerID != 0 && !owners.Contains(e.OwnerID)) {
entities.Remove(e);
continue;
}
RememberOwner(e);
if (e is BuildingPrivlidge priv) SetCurrentNameFromBuilding(priv);
if (e is Tugboat) SetCurrentNameFromVehicle(e);
if (e is DecayEntity decayEntity) SetupDecayEntity(decayEntity);
if (e is AutoTurret turret) Instance.timer.Once(invokeTime += 0.1f, () => SetupTurret(turret));
if (config.Abandoned.DespawnSecondsInactive <= 0f && !config.RemoveOwnershipZero) continue;
if (config.RemoveOwnership || config.RemoveOwnershipFromContainers && e is StorageContainer) e.OwnerID = 0;
}
var type = anchor.IsKilled() ? "BUILDING" : $"{anchor.GetType().Name.ToUpper()}";
if (!string.IsNullOrEmpty(previousName)) {
Puts("{0} - {1} ({2}) at {3} with {4} entities and {5} items", type, previousName, previousId, center, entities.Count, lootAmount);
}
else Puts("{0} - {1} with {2} entities and {3} items", type, center, entities.Count, lootAmount);
_coroutine = null;
}
private void SetCurrentNameFromBuilding(BuildingPrivlidge priv) {
if (!string.IsNullOrEmpty(currentName)) return;
if (priv.OwnerID.IsSteamId() && owners.Contains(priv.OwnerID)) {
var username = GetUserName(priv.OwnerID);
SetOwner(priv.OwnerID, username, priv.OwnerID, username);
}
privs.Add(priv);
}
private void SetCurrentNameFromVehicle(BaseEntity entity) {
if (!string.IsNullOrEmpty(currentName)) return;
var priv = Instance.GetVehiclePrivilege(entity.children);
if (priv == null || !priv.AnyAuthed()) return;
foreach (var auth in priv.authorizedPlayers) {
var username = GetUserName(auth.userid);
if (string.IsNullOrEmpty(username)) continue;
SetOwner(auth.userid, username, auth.userid, username);
break;
}
}
private void SetupEntities() {
_coroutine = ServerMgr.Instance.StartCoroutine(EntitySetup());
Instance.Subscribe();
}
private void SetupDecayEntity(DecayEntity e) {
buildingIDs.Add(e.buildingID);
}
public class TurretInfo {
public Item weapon;
public AutoTurret turret;
public List<Item> items = new();
}
private List<TurretInfo> turrets = new();
private void SetupTurret(AutoTurret turret) {
if (!config.Abandoned.AutoTurret.Enabled) {
return;
}
SetupIO(turret);
turret.InitializeHealth(config.Abandoned.AutoTurret.Health, config.Abandoned.AutoTurret.Health);
turret.sightRange = config.Abandoned.AutoTurret.SightRange;
turret.aimCone = config.Abandoned.AutoTurret.AimCone;
if (config.Abandoned.AutoTurret.RemoveWeapon) {
turret.AttachedWeapon = null;
Item slot = turret.inventory.GetSlot(0);
if (slot != null && (slot.info.category == ItemCategory.Weapon || slot.info.category == ItemCategory.Fun)) {
slot.RemoveFromContainer();
slot.Remove();
}
}
TurretInfo ti = new() {
turret = turret
}
;
if (config.Abandoned.AutoTurret.Shortnames.Count > 0) {
ActionIn(0.1f, () => {
if (!turret.IsKilled() && turret.AttachedWeapon == null) {
var shortname = config.Abandoned.AutoTurret.Shortnames.GetRandom();
var itemToCreate = ItemManager.FindItemDefinition(shortname);
if (itemToCreate != null) {
Item item = ItemManager.Create(itemToCreate, 1, (ulong)itemToCreate.skins.GetRandom().id);
if (!item.MoveToContainer(turret.inventory, 0, false)) {
item.Remove();
}
else {
item.SwitchOnOff(true);
ti.weapon = item;
}
}
}
}
);
}
ActionIn(1f, turret.UpdateAttachedWeapon);
ActionIn(2.5f, () => FillAmmoTurret(ti));
if (config.Abandoned.AutoTurret.Hostile) {
turret.SetPeacekeepermode(false);
}
if (!config.Abandoned.AutoTurret.RequiresPower && !turret.IsOnline()) {
ActionIn(3f, turre
// ... [Code preview truncated due to length. Full source in attached file] ...