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.0.8")]
[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 List<string> {
"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 List<string> {
"foundation", "foundation.triangle", "roof.triangle", "roof"
}
;
private List<string> TrueDamage = new List<string> {
"Barricade", "SimpleBuildingBlock", "IceFence", "TeslaCoil", "BaseTrap", "GunTrap", "FlameTurret", "FogMachine", "SamSite", "AutoTurret"
}
;
private Coroutine reportCoroutine;
private bool DebugMode {
get;
set;
}
private bool newSave {
get;
set;
}
private bool purgeDay {
get;
set;
}
private StoredData data {
get;
set;
}
= new StoredData();
private StringBuilder _sb {
get;
set;
}
= new StringBuilder();
private static AbandonedBases Instance {
get;
set;
}
private Coroutine abandonedCoroutine {
get;
set;
}
private List<ulong> AbandonedSleepers {
get;
set;
}
= new List<ulong>();
private List<string> _waitingList {
get;
set;
}
= new List<string>();
private List<UserConversion> _conversions {
get;
set;
}
= new List<UserConversion>();
private List<AbandonedBuilding> AbandonedBuildings {
get;
set;
}
= new List<AbandonedBuilding>();
private Dictionary<ulong, List<Notification>> _notifications {
get;
set;
}
= new Dictionary<ulong, List<Notification>>();
private Dictionary<ulong, AbandonedBuilding> AbandonedReferences {
get;
set;
}
= new Dictionary<ulong, AbandonedBuilding>();
private Dictionary<BuildingPrivlidge, List<BaseEntity>> _buildings {
get;
set;
}
= new Dictionary<BuildingPrivlidge, List<BaseEntity>>();
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 Dictionary<ulong, int>();
[JsonProperty("Cooldown Between Conversion", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenConversion {
get;
set;
}
= new Dictionary<ulong, DateTime>();
[JsonProperty("Cooldown Between Cancel", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenCancel {
get;
set;
}
= new Dictionary<ulong, DateTime>();
[JsonProperty("Cooldown Between Events", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, DateTime> CooldownBetweenEvents {
get;
set;
}
= new Dictionary<ulong, DateTime>();
[JsonProperty("Activities", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ActivityInfo> Activity {
get;
set;
}
= new List<ActivityInfo>();
public string LastRunTime {
get;
set;
}
= DateTime.MinValue.ToString();
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;
}
}
private class ActivityInfo {
[JsonProperty(PropertyName = "owners", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ulong> owners {
get;
set;
}
= new List<ulong>();
[JsonProperty(PropertyName = "permission")]
public string perm {
get;
set;
}
= string.Empty;
[JsonProperty(PropertyName = "total")]
public int total {
get;
set;
}
public ActivityInfo() {
}
public ActivityInfo(List<ulong> owners) {
this.owners = owners;
SetPermission();
}
public int GetLimit() {
if (string.IsNullOrEmpty(perm) || !perm.PermissionExists()) {
SetPermission();
}
foreach (var purge in config.Purges) {
if (purge.Permission.Equals(perm, StringComparison.OrdinalIgnoreCase)) {
if (purge.NoPurge) {
return 0;
}
return purge.Limit;
}
}
return 0;
}
private void SetPermission() {
var limit = int.MinValue;
foreach (var owner in this.owners) {
var purge = PurgeSettings.Find(owner);
if (purge?.Permission == null) {
continue;
}
if (purge.Limit > limit) {
perm = purge.Permission;
limit = purge.Limit;
}
if (purge.Limit <= 0) {
perm = purge.Permission;
break;
}
}
}
public bool SameOwners(List<ulong> owners) {
foreach (var owner in this.owners) {
if (!owners.Contains(owner)) {
return false;
}
}
return true;
}
public static ActivityInfo Find(List<ulong> owners, List<ActivityInfo> activity) {
ActivityInfo result = null;
int j = int.MinValue;
foreach (var other in activity) {
if (other.SameOwners(owners)) {
int k = other.GetLimit();
if (k > j) {
result = other;
j = k;
}
}
}
if (result == null) {
result = new ActivityInfo(owners);
}
return result;
}
}
private class DelaySettings {
public static Dictionary<ulong, DelaySettings> PvpDelay {
get;
set;
}
= new Dictionary<ulong, DelaySettings>();
public AbandonedBuilding Building {
get;
set;
}
public Timer Timer {
get;
set;
}
public static void Remove(BasePlayer player) {
DelaySettings ds;
if (PvpDelay.TryGetValue(player.userID, out ds)) {
if (ds.Timer != null && !ds.Timer.Destroyed) {
ds.Timer.Callback.Invoke();
ds.Timer.Destroy();
}
PvpDelay.Remove(player.userID);
}
}
public static void Add(AbandonedBuilding abandonedBuilding, BasePlayer player) {
if (config.Abandoned.PVPDelay <= 0f) {
Message(player, abandonedBuilding.AllowPVP ? "OnPlayerExit" : "OnPlayerExitPVE");
return;
}
DelaySettings ds;
if (!PvpDelay.TryGetValue(player.userID, out ds)) {
ulong userid = player.userID;
Interface.CallHook("OnPlayerPvpDelayStart", player, userid, abandonedBuilding.center, abandonedBuilding.intruders, abandonedBuilding.entities);
PvpDelay[userid] = ds = new DelaySettings {
Timer = abandonedBuilding.timer.Once(config.Abandoned.PVPDelay, () => {
Interface.CallHook("OnPlayerPvpDelayExpiredII", player, userid, abandonedBuilding.center, abandonedBuilding.intruders, abandonedBuilding.entities);
PvpDelay.Remove(userid);
}
), Building = abandonedBuilding
}
;
Message(player, "DoomAndGloom", _("PVPFlag", player.UserIDString), config.Abandoned.PVPDelay);
}
else ds.Timer.Reset();
}
}
private class AbandonedBuilding : FacepunchBehaviour {
internal class EntityOwner {
public BaseEntity Entity;
public ulong OwnerID;
public EntityOwner(BaseEntity entity) {
Entity = entity;
OwnerID = entity.OwnerID;
}
}
internal List<ulong> IsAllowed {
get;
set;
}
= new List<ulong>();
internal List<ulong> Messages {
get;
set;
}
= new List<ulong>();
internal HashSet<uint> buildingIDs {
get;
set;
}
= new HashSet<uint>();
internal List<BuildingPrivlidge> privs {
get;
set;
}
= new List<BuildingPrivlidge>();
internal List<ulong> owners {
get;
set;
}
= new List<ulong>();
internal List<BaseEntity> entities {
get;
set;
}
= new List<BaseEntity>();
internal List<SphereEntity> spheres {
get;
set;
}
= new List<SphereEntity>();
internal List<Vector3> compound {
get;
set;
}
= new List<Vector3>();
internal List<Vector3> foundations {
get;
set;
}
= new List<Vector3>();
internal List<BasePlayer> intruders {
get;
set;
}
= new List<BasePlayer>();
internal List<StorageContainer> containers {
get;
set;
}
= new List<StorageContainer>();
internal Dictionary<ulong, EntityOwner> EntityOwners {
get;
set;
}
= new Dictionary<ulong, EntityOwner>();
internal MapMarkerGenericRadius genericMarker {
get;
set;
}
= null;
internal VendingMachineMapMarker vendingMarker {
get;
set;
}
= null;
internal bool IsOwnerLocked {
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 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 List<string>();
internal GameObject go {
get;
set;
}
internal float cancelCooldownTime {
get;
set;
}
internal bool isCanceled {
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 MyInstance {
get;
set;
}
internal bool LockBaseToFirstAttacker => AllowPVP ? config.Abandoned.LockBaseToFirstAttackerPVP : config.Abandoned.LockBaseToFirstAttackerPVE;
internal bool EjectFromLockedBase => AllowPVP ? config.Abandoned.EjectLockedPVP : config.Abandoned.EjectLockedPVE;
internal string GetGrid() => PhoneController.PositionToGridCoord(center);
internal Vector3 _center {
get;
set;
}
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 static bool AddRange(List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> foundations, List<Vector3> compound, List<Vector3> walls) {
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 (config.Abandoned.Twig || (e as BuildingBlock).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 (config.Abandoned.Twig || (e as BuildingBlock)?.grade != BuildingGrade.Enum.Twigs) {
walls.Add(e.transform.position);
}
}
else if (Instance.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 < config.Abandoned.FoundationLimit || walls.Count < config.Abandoned.WallLimit) {
return false;
}
return true;
}
public static bool AddRange(List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> compound, ref int loot) {
List<BaseEntity.Slot> _checkSlots = new List<BaseEntity.Slot> {
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 config.Abandoned.Tugboats.Loot <= 0 || loot >= config.Abandoned.Tugboats.Loot;
}
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;
}
SetOwner(player.userID, player.displayName, player.userID, player.displayName);
}
public void TrySetOwnerLock(BasePlayer attacker) {
if (IsOwnerLocked && !CanBypass(attacker) && !IsAlly(attacker)) {
TryEjectFromLockedBase(attacker);
return;
}
if (!canReassign || !LockBaseToFirstAttacker || !attacker.IsHuman() || owners.Contains(attacker.userID) || MyInstance.HasEventCooldown(attacker, this) || IsAlly(attacker)) {
return;
}
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 CanDoActions(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 || 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;
RaycastHit hit;
if (Physics.Raycast(position + new Vector3(0f, y, 0f), Vector3.down, out 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 instance, 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) {
MyInstance = instance;
if (EjectFromLockedBase) {
timer.Every(1f, Protector);
}
TrySetOwner(user);
markerCreated = user == null && !config.Abandoned.AutoMarkers || user != null && !config.Abandoned.ManualMarkers;
canReassign = currentId == 0 || string.IsNullOrEmpty(currentName);
MyInstance.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(Refresh);
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 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) => MyInstance.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 (CanDoActions(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) {
var player = entity as BasePlayer;
if (!intruders.Contains(player)) {
intruders.Add(player);
Message(player, AllowPVP ? "OnPlayerEntered" : "OnPlayerEnteredPVE");
Interface.CallHook("OnPlayerEnteredAbandonedBase", player, transform.position, AllowPVP, center, intruders, entities);
}
}
else if (entity is BaseMountable) {
var m = entity as BaseMountable;
GetMountedPlayers(m).ForEach(player => {
if (!intruders.Contains(player)) {
intruders.Add(player);
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) {
var player = entity as BasePlayer;
OnPlayerExit(player, player.IsDead());
}
else if (entity is BaseMountable) {
var m = entity as BaseMountable;
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 BasePlayer)) continue;
var target = entity as BasePlayer;
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);
}
}
}
}
private void RewardPlayers() {
var raiders = intruders.Where(player => !IsPlayerIneligible(player));
foreach (var player in raiders) {
if (!isCanceled) {
MyInstance.GiveRewards(player, raiders.Count);
}
if (!DisableEventCooldown) {
SetEventCooldown(player.userID);
}
}
if (isCanceled) {
return;
}
var players = string.Join(", ", raiders.Select(x => x.displayName));
var text = $"{currentName} ({currentId}) has raided the base with {players} at {center} ({GetGrid()}) owned by {previousName} ({previousId})";
if (raiders.Count == 0) text = $"{currentName} ({currentId}) base has been abandoned at {center} ({GetGrid()}) by {previousName} ({previousId})";
if (config.UseLogFile && previousId != 0uL && previousId != currentId) {
MyInstance.LogToFile("sar", text, MyInstance, false, true);
}
Puts(text);
}
private bool IsPlayerIneligible(BasePlayer player) {
return player == null || player.IsFlying || player.limitNetworking || config.Abandoned.RemoveAdminRaiders && player.IsAdmin;
}
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() => MyInstance.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 (config?.Abandoned.CooldownBetweenConversion > 0 && !userid.HasPermission("abandonedbases.convert.nocooldown")) {
MyInstance.data.CooldownBetweenConversion[userid] = DateTime.Now.AddSeconds(config.Abandoned.CooldownBetweenConversion);
}
}
public void SetCancelCooldown(ulong userid) {
if (config?.Abandoned.CooldownBetweenCancel > 0 && !userid.HasPermission("abandonedbases.convert.cancel.nocooldown")) {
MyInstance.data.CooldownBetweenCancel[userid] = DateTime.Now.AddSeconds(config.Abandoned.CooldownBetweenCancel);
}
}
public void SetEventCooldown(ulong userid) {
if (config?.Abandoned.CooldownBetweenEvents > 0 && !userid.HasPermission("abandonedbases.noeventcooldown")) {
MyInstance.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 && MyInstance.Economics.CanCall()) {
if (Convert.ToBoolean(MyInstance.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 && MyInstance.ServerRewards.CanCall()) {
if (Convert.ToBoolean(MyInstance.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 (MyInstance.purgeDay) {
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 (entity is DecayEntity) {
var decayEntity = entity as DecayEntity;
if (buildingIDs.Contains(decayEntity.buildingID)) {
return true;
}
}
var priv = entity.GetBuildingPrivilege();
if (priv == null) {
return NearCompound(entity.transform.position);
}
return buildingIDs.Contains(priv.buildingID);
}
public void RememberOwner(BaseEntity entity) {
if (entity.OwnerID == 0 || owners.Contains(entity.OwnerID)) {
MyInstance.AbandonedReferences[entity.net.ID.Value] = this;
EntityOwners[entity.net.ID.Value] = new EntityOwner(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 BaseCombatEntity) TryInvokeMethod(() => SetupHealth(e as BaseCombatEntity));
if (e is BuildingPrivlidge) SetCurrentNameFromBuilding(e);
if (e is Tugboat) SetCurrentNameFromVehicle(e);
if (e is DecayEntity) SetupDecayEntity(e as DecayEntity);
if (e is AutoTurret) MyInstance.timer.Once(invokeTime += 0.1f, () => SetupTurret(e as AutoTurret));
if (config.Abandoned.DespawnTime <= 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(BaseEntity entity) {
if (!string.IsNullOrEmpty(currentName)) return;
if (entity.OwnerID.IsSteamId() && owners.Contains(entity.OwnerID)) {
var username = GetUserName(entity.OwnerID);
SetOwner(entity.OwnerID, username, entity.OwnerID, username);
}
privs.Add(entity as BuildingPrivlidge);
}
private void SetCurrentNameFromVehicle(BaseEntity entity) {
if (!string.IsNullOrEmpty(currentName)) return;
var priv = MyInstance.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());
MyInstance.Subscribe();
}
private void SetupDecayEntity(DecayEntity e) {
buildingIDs.Add(e.buildingID);
}
/*private void SetupHealth(BaseCombatEntity e)
{
if (e is BuildingBlock)
{
BuildingBlockOptions bbo;
if (config.Abandoned.Blocks.TryGetValue(e.ShortPrefabName, out bbo))
{
var block = e as BuildingBlock;
if (block.IsKilled()) return;
block.currentGrade.construction.healthMultiplier = 1f;
block.SetHealthToMax();
System.Reflection.MethodInfo MemberwiseCloneMethod = typeof(object).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
Construction construction = (Construction)MemberwiseCloneMethod.Invoke(block.blockDefinition, null);
ConstructionGrade[] grades = (ConstructionGrade[])MemberwiseCloneMethod.Invoke(block.blockDefinition.grades, null);
block.blockDefinition = construction;
block.blockDefinition.grades = grades;
switch (block.grade)
{
case BuildingGrade.Enum.Twigs: block.currentGrade.gradeBase.baseHealth = bbo.Twigs; break;
case BuildingGrade.Enum.Wood: block.currentGrade.gradeBase.baseHealth = bbo.Wooden; break;
case BuildingGrade.Enum.Stone: block.currentGrade.gradeBase.baseHealth = bbo.Stone; break;
case BuildingGrade.Enum.Metal: block.currentGrade.gradeBase.baseHealth = bbo.Metal; break;
case BuildingGrade.Enum.TopTier: block.currentGrade.gradeBase.baseHealth = bbo.HQM; break;
}
float multiplier;
if (config.Abandoned.BlocksMultiplier.TryGetValue(block.grade, out multiplier))
{
block.currentGrade.construction = (Construction)MemberwiseCloneMethod.Invoke(block.currentGrade.construction, null);
block.currentGrade.construction
// ... [Code preview truncated due to length. Full source in attached file] ...