C#:
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", "https://discord.gg/NUXhMfyc", "2.0.2")]
[Description("Allows bases to become raidable when the owner becomes inactive")]
public class AbandonedBases : RustPlugin {
#region Variables
private new const string Name = "Abandoned Bases";
[PluginReference]
Plugin Economics, ServerRewards, IQEconomic, RaidableBases, Clans, Friends, Notify, AdvancedAlerts, ZoneManager, SkillTree;
private void OnNewSave(string filename) {
newSave = true;
}
private void Init() {
Instance = this;
TryInvokeMethod(Unsubscribe);
}
private void OnServerInitialized(bool isStartup) {
TryInvokeMethod(RegisterPermissions);
TryInvokeMethod(RegisterCommands);
TryInvokeMethod(LoadData);
TryInvokeMethod(config.Validate);
TryInvokeMethod(UpdateLastSeen);
TryInvokeMethod(StartNotificationTimer);
TryInvokeMethod(BlockZoneManagerZones);
SetupAbandonedRoutine();
}
private void Unload() {
TryInvokeMethod(StopAbandonedCoroutine);
TryInvokeMethod(SaveData);
TryInvokeMethod(DestroyAll);
TryInvokeMethod(DelaySettings.Clear);
data = null;
config = null;
Instance = null;
AbandonedBasesExtensionMethods.ExtensionMethods.p = null;
}
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 enum Response {
Delete, Success, Skipped, Unknown
}
private Coroutine reportCoroutine;
private bool DebugMode {
get;
set;
}
private bool newSave {
get;
set;
}
private bool purgeDay {
get;
set;
}
private StoredData data {
get;
set;
}
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<AbandonedBuilding> AbandonedBuildings {
get;
set;
}
= new List<AbandonedBuilding>();
private Dictionary<uint, AbandonedBuilding> AbandonedReferences {
get;
set;
}
= new Dictionary<uint, AbandonedBuilding>();
private Dictionary<BuildingPrivlidge, List<BaseEntity>> _buildings {
get;
set;
}
= new Dictionary<BuildingPrivlidge, List<BaseEntity>>();
private Dictionary<ulong, List<Notification>> _notifications = new Dictionary<ulong, List<Notification>>();
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", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, float> Cooldowns {
get;
set;
}
= new Dictionary<ulong, float>();
[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(List<CustomCostOptions> options, BasePlayer player) {
userId = player.userID;
this.player = player;
this.options = options;
}
public Payment(double cost, BasePlayer player) {
userId = player.userID;
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 aipermission {
get;
set;
}
= string.Empty;
public int total {
get;
set;
}
public ActivityInfo() {
}
public ActivityInfo(List<ulong> owners) {
if (!owners.IsNullOrEmpty()) {
this.owners = owners;
int limit = int.MinValue;
foreach (ulong ownerId in this.owners) {
var purge = PurgeSettings.Find(ownerId.ToString());
if (purge?.Limit > limit) {
aipermission = string.IsNullOrEmpty(purge.Permission) ? string.Empty : purge.Permission.ToLower();
limit = purge.Limit;
}
}
}
else this.owners = new List<ulong>();
}
public bool IsExpired() {
if (string.IsNullOrEmpty(aipermission)) {
return true;
}
foreach (var purge in config.Purges) {
if (aipermission.Equals(purge.Permission, StringComparison.OrdinalIgnoreCase)) {
return total >= purge?.Limit;
}
}
return true;
}
public bool Equals(List<ulong> owners) {
if (owners.IsNullOrEmpty()) return false;
//this.owners.IsNullOrEmpty();
foreach (ulong ownerId in this.owners) {
if (ownerId.HasPermission("abandonedbases.exclude")) {
continue;
}
if (!owners.Contains(ownerId)) {
return false;
}
}
return true;
}
public static ActivityInfo Find(List<ulong> owners, List<ActivityInfo> source) {
foreach (var ai in source) {
if (ai.Equals(owners)) {
return ai;
}
}
return new ActivityInfo(owners);
}
}
private class DelaySettings {
private static Dictionary<ulong, DelaySettings> PvpDelay {
get;
set;
}
= new Dictionary<ulong, DelaySettings>();
public AbandonedBuilding Building {
get;
set;
}
public Timer Timer {
get;
set;
}
public static bool TryGetValue(ulong userid, out DelaySettings delaySettings) {
return PvpDelay.TryGetValue(userid, out delaySettings);
}
public static bool Has(ulong userid) {
return PvpDelay.ContainsKey(userid);
}
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;
PvpDelay[userid] = ds = new DelaySettings {
Timer = Instance.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);
return;
}
ds.Timer.Reset();
}
public static void Clear() => PvpDelay.Clear();
}
private class AbandonedBuilding : FacepunchBehaviour {
public HashSet<uint> buildingIDs {
get;
set;
}
= new HashSet<uint>();
public List<BaseEntity> entities {
get;
set;
}
= new List<BaseEntity>();
private List<SphereEntity> spheres {
get;
set;
}
= new List<SphereEntity>();
private List<Vector3> foundations {
get;
set;
}
= new List<Vector3>();
public List<BasePlayer> intruders {
get;
set;
}
= new List<BasePlayer>();
private List<StorageContainer> containers {
get;
set;
}
= new List<StorageContainer>();
private Dictionary<BaseEntity, ulong> _entityOwners {
get;
set;
}
= new Dictionary<BaseEntity, ulong>();
private MapMarkerGenericRadius genericMarker {
get;
set;
}
= null;
private VendingMachineMapMarker vendingMarker {
get;
set;
}
= null;
private PluginTimers timer {
get;
set;
}
public Vector3 center {
get;
set;
}
private float DespawnTime {
get;
set;
}
private float radius {
get;
set;
}
private bool markerCreated {
get;
set;
}
public bool killed {
get;
set;
}
public bool AllowPVP {
get;
set;
}
public bool IsExpired {
get;
set;
}
private SphereCollider _collider {
get;
set;
}
private Payment payment {
get;
set;
}
private List<string> groups = new List<string>();
private GameObject go {
get;
set;
}
private float cancelCooldownTime {
get;
set;
}
private bool isCanceled {
get;
set;
}
public bool InRange(Vector3 target) => AbandonedBases.InRange(target, center, radius);
public static bool AddRange(List<BaseEntity> entities, List<StorageContainer> containers, List<Vector3> foundations, List<Vector3> walls) {
var floors = new List<Vector3>();
foreach (var e in entities) {
if (e.ShortPrefabName == "foundation" || e.ShortPrefabName == "foundation.triangle") {
if (config.Abandoned.Twig || (e as BuildingBlock).grade != BuildingGrade.Enum.Twigs) {
foundations.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) // support for WaterBases plugin
{
foundations = floors;
}
if (foundations.Count == 0 || foundations.Count < config.Abandoned.FoundationLimit || walls.Count < config.Abandoned.WallLimit) {
return false;
}
return true;
}
public void Setup(PluginTimers timer, List<Vector3> foundations, List<BaseEntity> entities, List<StorageContainer> containers, List<ulong> ownerIds, Vector3 center, Payment payment, float radius) {
Instance.AbandonedBuildings.Add(this);
AllowPVP = config.Abandoned.AllowPVP;
this.foundations = foundations;
this.containers = containers;
this.entities = entities;
this.payment = payment;
this.center = center;
this.radius = radius;
this.timer = timer;
Interface.Oxide.NextTick(() => {
TryInvokeMethod(SetupEntities);
TryInvokeMethod(SetupPosition);
TryInvokeMethod(SetupCollider);
TryInvokeMethod(CompleteConvertPayment);
TryInvokeMethod(Announce);
TryInvokeMethod(Refresh);
TryInvokeMethod(SpawnNpcs);
TryInvokeMethod(SetupSleepers);
UpdateActivity(ownerIds);
Puts("Opened abandoned player base at {0} with {1} entities", center, entities.Count);
cancelCooldownTime = Time.time + config.Abandoned.CancelCooldown;
}
);
}
private void Awake() {
go = gameObject;
go.name = "AbandonedBase";
}
private void OnDestroy() {
if (groups.Count > 0) {
Plugin BotReSpawn = Interface.Oxide.RootPluginManager.GetPlugin("BotReSpawn");
groups.ForEach(group => BotReSpawn?.Call("RemoveGroupSpawn", group));
}
}
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() {
if (killed) {
return;
}
Interface.CallHook("OnAbandonedBaseEnded", center, AllowPVP, intruders, entities);
killed = true;
TryInvokeMethod(RewardPlayers);
TryInvokeMethod(RestoreEntityOwners);
TryInvokeMethod(RemoveReferences);
TryInvokeMethod(CancelInvokes);
TryInvokeMethod(KillMarkers);
TryInvokeMethod(KillSpheres);
Destroy(go);
Destroy(this);
}
private void SetupSleepers() {
if (!config.KillInactiveSleepers || !config.MoveInventory || payment == null) {
return;
}
var boxes = containers.Where(x => !x.IsKilled() && IsBox(x) && !x.inventory.IsFull());
if (boxes.Count == 0) {
return;
}
var sleepers = entities.OfType<BasePlayer>().Where(x => x.IsSleeping() && !x.IsConnected && x.inventory != null);
if (sleepers.Count == 0) {
return;
}
foreach (var sleeper in sleepers) {
var items = sleeper.inventory.AllItems().ToList();
if (items.Count == 0) continue;
var box = boxes.GetRandom();
while (!box.inventory.IsFull() && items.Count > 0) {
Item item = items[0];
if (item == null) continue;
if (config.MoveInventoryBlacklist.Contains(item.info.shortname)) {
item.RemoveFromContainer();
item.Remove(0f);
}
else item.MoveToContainer(box.inventory);
items.Remove(item);
}
if (box.inventory.IsFull()) boxes.Remove(box);
if (boxes.Count == 0) break;
}
}
private void RewardPlayers() {
foreach (var player in intruders) {
if (player != null) {
Rewards.Give(player, intruders.Count);
}
}
}
private void RestoreEntityOwners() {
if (IsExpired || _entityOwners.Count == 0) {
return;
}
foreach (var element in _entityOwners.ToList()) {
if (element.Key.IsKilled()) continue;
element.Key.OwnerID = element.Value;
}
}
private void RemoveReferences() => Instance?.RemoveReferences(this, entities);
private void CancelInvokes() {
try {
CancelInvoke();
}
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);
private void CompletePayment(Payment payment, bool 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);
}
if (cancel) {
isCanceled = config.Abandoned.Rewards.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.FindItemIDs(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;
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.purgeDay) {
return;
}
var grid = PositionToGrid(center);
foreach (var target in BasePlayer.activePlayerList) {
if (target.HasPermission("abandonedbases.notices")) {
Message(target, "Abandoned", grid);
}
}
}
private bool NearFoundation(Vector3 a) {
for (int i = 0; i < foundations.Count; i++) {
if (AbandonedBases.InRange(foundations[i], a, 3f)) {
return true;
}
}
return false;
}
public bool IsAttached(BaseEntity entity) {
if (entity is DecayEntity) {
var decayEntity = entity as DecayEntity;
return buildingIDs.Contains(decayEntity.buildingID);
}
var priv = entity.GetBuildingPrivilege();
if (priv == null) {
return NearFoundation(entity.transform.position);
}
return buildingIDs.Contains(priv.buildingID);
}
public void RememberOwner(BaseEntity entity) {
Instance.AbandonedReferences[entity.net.ID] = this;
_entityOwners[entity] = entity.OwnerID;
}
private void SetupEntities() {
float invokeTime = 0f;
foreach (var e in entities) {
if (e.IsKilled() || e.net == null) continue;
//if (e is BaseCombatEntity) TryInvokeMethod(() => SetupHealth(e as BaseCombatEntity));
RememberOwner(e);
if (e is DecayEntity) SetupDecayEntity(e as DecayEntity);
if (e is AutoTurret) Instance.timer.Once(invokeTime += 0.1f, () => SetupTurret(e as AutoTurret));
if (config.Abandoned.DespawnTime <= 0f) continue;
if (config.RemoveOwnership || config.RemoveOwnershipFromContainers && e is StorageContainer) e.OwnerID = 0;
}
Instance.Subscribe();
}
private void SetupDecayEntity(DecayEntity e) {
buildingIDs.Add(e.buildingID);
}
/*private void SetupHealth(BaseCombatEntity e)
{
BuildingBlockOptions bbo;
if (config.Abandoned.Blocks.TryGetValue(e.ShortPrefabName, out bbo))
{
var block = e as BuildingBlock;
if (block.IsKilled()) return;
switch (block.grade)
{
case BuildingGrade.Enum.Twigs: block.health = bbo.Twigs; break;
case BuildingGrade.Enum.Wood: block.health = bbo.Wooden; break;
case BuildingGrade.Enum.Stone: block.health = bbo.Stone; break;
case BuildingGrade.Enum.Metal: block.health = bbo.Metal; break;
case BuildingGrade.Enum.TopTier: block.health = bbo.HQM; break;
}
}
else
{
float value;
if (config.Abandoned.Other.TryGetValue(e.ShortPrefabName, out value))
{
e.health = value;
}
}
}*/
private void SetupTurret(AutoTurret turret) {
if (!config.Abandoned.AutoTurret.Enabled) {
return;
}
SetupIO(turret);
if (payment == null && config.RemoveOwnership) turret.authorizedPlayers.Clear();
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();
}
}
if (config.Abandoned.AutoTurret.Shortnames.Count > 0) {
if (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();
}
}
}
}
InvokeEx(turret.UpdateAttachedWeapon, 0.1f);
InvokeEx(() => FillAmmoTurret(turret), 0.2f);
if (config.Abandoned.AutoTurret.Hostile) {
turret.SetPeacekeepermode(false);
}
if (!config.Abandoned.AutoTurret.RequiresPower) {
InvokeEx(turret.InitiateStartup, 0.3f);
}
if (config.Abandoned.AutoTurret.InfiniteAmmo) {
turret.inventory.onPreItemRemove += new Action<Item>(OnWeaponItemPreRemove);
}
}
private void InvokeEx(Action action, float time) {
timer.Once(time, () => {
try {
action.Invoke();
}
catch {
}
}
);
}
private void OnWeaponItemPreRemove(Item item) {
var weapon = item.parent?.entityOwner;
if (weapon is AutoTurret) {
var turret = weapon as AutoTurret;
InvokeEx(() => FillAmmoTurret(turret), 0.1f);
}
}
private void FillAmmoTurret(AutoTurret turret) {
if (turret.IsDestroyed) {
return;
}
if (turret.authorizedPlayers.Exists(x => x.userid.IsSteamId())) {
return;
}
var attachedWeapon = turret.GetAttachedWeapon();
if (attachedWeapon == null) {
return;
}
int p = Math.Max(config.Abandoned.AutoTurret.Ammo, attachedWeapon.primaryMagazine.capacity);
turret.inventory.AddItem(attachedWeapon.primaryMagazine.ammoType, p, 0uL);
attachedWeapon.primaryMagazine.contents = attachedWeapon.primaryMagazine.capacity;
attachedWeapon.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
InvokeEx(turret.UpdateTotalAmmo, 0.25f);
}
private void SetupIO(ContainerIOEntity io) {
io.dropsLoot = false;
io.inventory.SetFlag(ItemContainer.Flag.NoItemInput, true);
if (config.Abandoned.AutoTurret.HasPower) {
io.SetFlag(BaseEntity.Flags.Reserved8, true, false, true);
}
}
private void SetupPosition() {
if (radius <= 0 || radius < config.Abandoned.MinCustomSphereRadius) {
if (config.Abandoned.Dynamic) {
radius = Mathf.Min(config.Abandoned.MaxDynamicRadius, Mathf.Max(config.Abandoned.SphereRadius, GetRadiusFromMultiplePoints(GetPositions(), center)));
}
else radius = config.Abandoned.SphereRadius;
}
go.transform.position = center;
}
private void SetupCollider() {
_collider = gameObject.GetComponent<SphereCollider>() ?? gameObject.AddComponent<SphereCollider>();
_collider.radius = radius;
_collider.isTrigger = true;
_collider.center = Vector3.zero;
gameObject.layer = (int)Layer.Trigger;
}
public void Refresh() {
if (config.Abandoned.DespawnTime > 0f) {
CancelInvoke(DestroyAll);
Invoke(DestroyAll, config.Abandoned.DespawnTime);
DespawnTime = Time.realtimeSinceStartup + config.Abandoned.DespawnTime;
}
if (config.Abandoned.Markers) CreateMarkers();
else markerCreated = true;
}
public void UpdateActivity(List<ulong> ownerIds) {
if (config.Abandoned.DoNotDestroyManual && payment != null) {
IsExpired = false;
return;
}
if (!config.Abandoned.DoNotDestroy) {
var activity = ActivityInfo.Find(ownerIds, Instance.data.Activity);
if (!Instance.data.Activity.Contains(activity)) {
Instance.data.Activity.Add(activity);
}
activity.total++;
if (IsExpired = activity.IsExpired()) {
Instance.data.Activity.Remove(activity);
}
}
}
private void SpawnNpcs() {
var profiles = config.Abandoned.BotSpawnProfileNames;
profiles.Remove("profile_name_1");
profiles.Remove("profile_name_2");
if (profiles.Count == 0) {
return;
}
string group = Guid.NewGuid().ToString();
Plugin BotReSpawn = Interface.Oxide.RootPluginManager.GetPlugin("BotReSpawn");
if (BotReSpawn == null) {
return;
}
groups.Add(group);
BotReSpawn?.Call("AddGroupSpawn", center, profiles.GetRandom(), group, 0);
}
private List<Vector3> GetPositions() {
var positions = new List<Vector3>();
entities.RemoveAll(e => e.IsKilled());
foreach (var e in entities) {
if (e.prefabID == 3234260181 || e.prefabID == 72949757 || e is SimpleBuildingBlock) {
positions.Add(e.transform.position);
}
}
return positions;
}
private void CreateMarkers() {
if (markerCreated) {
return;
}
genericMarker = GameManager.server.CreateEntity(StringPool.Get(2849728229), center) as MapMarkerGenericRadius;
genericMarker.alpha = 0.75f;
genericMarker.color1 = Color.magenta;
genericMarker.color2 = Color.magenta;
genericMarker.radius = 0.25f;
genericMarker.Spawn();
vendingMarker = GameManager.server.CreateEntity(StringPool.Get(3459945130), center) as VendingMachineMapMarker;
vendingMarker.enabled = false;
vendingMarker.Spawn();
if (config.Abandoned.SphereAmount > 0) {
for (int i = 0; i < config.Abandoned.SphereAmount; i++) {
var sphere = GameManager.server.CreateEntity(StringPool.Get(3211242734), center) as SphereEntity;
sphere.currentRadius = 1f;
sphere.Spawn();
sphere.LerpRadiusTo(radius * 2f, radius * 0.75f);
spheres.Add(sphere);
}
}
UpdateMarkers();
markerCreated = true;
}
private void UpdateMarkers() {
if (genericMarker != null) {
genericMarker.SendUpdate();
}
if (vendingMarker != null) {
if (DespawnTime == 0f) {
vendingMarker.markerShopName = config.Abandoned.MarkerNameSeconds.Replace(" [{time}m]", string.Empty);
vendingMarker.SendNetworkUpdate();
Invoke(UpdateMarkers, 10f);
return;
}
var ts = TimeSpan.FromSeconds(DespawnTime - Time.realtimeSinceStartup);
if (ts.TotalMinutes >= 1) {
string despawnText = Math.Floor(ts.TotalMinutes).ToString();
vendingMarker.markerShopName = config.Abandoned.MarkerName.Replace("{time}", despawnText);
Invoke(UpdateMarkers, 10f);
}
else {
string despawnText = Math.Floor(ts.TotalSeconds).ToString();
vendingMarker.markerShopName = config.Abandoned.MarkerNameSeconds.Replace("{time}", despawnText);
Invoke(UpdateMarkers, 1f);
}
vendingMarker.SendNetworkUpdate();
}
}
private void DestroyAll() {
if (IsExpired) {
UndoLoop(entities, hookObjects);
}
DestroyMe();
}
public static void UndoLoop(ListHashSet<DecayEntity> decayEntities, object[] hookObjects, int count = 0) {
var entities = new List<BaseEntity>();
foreach (DecayEntity decayEntity in decayEntities) {
entities.Add(decayEntity);
}
UndoLoop(entities, hookObjects, count);
}
public static void UndoLoop(List<BaseEntity> entities, object[] hookObjects, int count = 0) {
entities.RemoveAll(e => e.IsKilled() || e.HasParent());
entities.Sort((x, y) => (x is BuildingBlock).CompareTo(y is BuildingBlock));
entities.Take(10).ToList().ForEach(entity => {
entities.Remove(entity);
if (entity is IOEntity) {
var io = entity as IOEntity;
io.ClearConnections();
if (entity is SamSite) {
(io as SamSite).staticRespawn = false;
}
}
if (entity is IItemContainerEntity) {
(entity as IItemContainerEntity)?.inventory?.Clear();
}
entity.SafelyKill();
}
);
if (count != 0 && entities.Count != 0 && entities.Count == count) {
goto done;
}
if (entities.Count > 0) {
Interface.Oxide.NextTick(() => UndoLoop(entities, hookObjects, entities.Count));
return;
}
done : if (hookObjects != null) {
Interface.CallHook("OnAbandonedBaseDespawned", hookObjects);
}
}
internal object[] hookObjects => new object[] {
center, AllowPVP, intruders, entities
}
;
public void OnPlayerExit(BasePlayer player, bool skipDelay) {
if (player == null || !player.IsHuman()) {
return;
}
intruders.Remove(player);
Interface.CallHook("OnPlayerExitAbandonedBase", player, center, AllowPVP, intruders, entities);
if (skipDelay || !AllowPVP) {
return;
}
DelaySettings.Add(this, player);
}
public static List<BasePlayer> GetMountedPlayers(BaseMountable m) {
BaseVehicle vehicle = m.HasParent() ? m.VehicleParent() : m as BaseVehicle;
if (!vehicle.IsRealNull()) {
return GetMountedPlayers(vehicle);
}
var players = new List<BasePlayer>();
var player = m.GetMounted();
if (!player.IsRealNull() && player.IsHuman()) {
players.Add(player);
}
return players;
}
private static List<BasePlayer> GetMountedPlayers(BaseVehicle vehicle) {
var players = new HashSet<BasePlayer>();
if (!vehicle.HasMountPoints()) {
var player = vehicle.GetMounted();
if (player?.IsHuman() == true) {
players.Add(player);
}
return players.ToList();
}
foreach (var mountPoint in vehicle.allMountPoints) {
var player = mountPoint.mountable?.GetMounted();
if (player?.IsHuman() == true) {
players.Add(player);
}
}
return players.ToList();
}
public static bool IsBox(BaseEntity entity) {
return entity.ShortPrefabName == "box.wooden.large" || entity.ShortPrefabName == "woodbox_deployed" || entity.ShortPrefabName == "coffinstorage";
}
public bool HasCancelCooldown(IPlayer user) {
if (cancelCooldownTime > Time.time) {
user.Reply(_("CancelCooldown", user.Id, Math.Round(cancelCooldownTime - Time.time, 2)));
return true;
}
return false;
}
public bool IsCompleted() {
foreach (var container in containers) {
if (!container.IsKilled() && !container.inventory.IsEmpty()) {
return false;
}
}
return true;
}
public static AbandonedBuilding Get(BasePlayer victim, HitInfo hitInfo) {
DelaySettings ds;
if (DelaySettings.TryGetValue(victim.userID, out ds) && ds.Building != null) {
return CompareTo(ds.Building, Get(hitInfo));
}
return CompareTo(Get(victim.transform.position), Get(hitInfo));
}
public static AbandonedBuilding Get(BaseEntity victim, HitInfo hitInfo) {
return CompareTo(Get(victim.transform.position), Get(hitInfo));
}
public static AbandonedBuilding CompareTo(AbandonedBuilding v, AbandonedBuilding a) {
if (v == null) return a;
if (a == null) return v;
if (v == a) return v;
return null;
}
private static AbandonedBuilding Get(HitInfo hitInfo) {
if (hitInfo?.Initiator?.IsDestroyed == false) {
var abandonedBuilding = Get(hitInfo.Initiator.ServerPosition);
if (abandonedBuilding != null) return abandonedBuilding;
}
if (hitInfo?.WeaponPrefab?.IsDestroyed == false) {
var abandonedBuilding = Get(hitInfo.WeaponPrefab.ServerPosition);
if (abandonedBuilding != null) return abandonedBuilding;
}
if (hitInfo?.Weapon?.IsDestroyed == false) {
return Get(hitInfo.Weapon.ServerPosition);
}
return null;
}
public static AbandonedBuilding Get(Vector3 target) {
foreach (var abandonedBuilding in Instance.AbandonedBuildings) {
if (abandonedBuilding.InRange(target)) {
return abandonedBuilding;
}
}
return null;
}
}
#endregion
#region Hooks
private void OnPlayerConnected(BasePlayer player) {
UpdateLastSeen(player);
UpdateInvoke(player);
}
private void OnPlayerSleep(BasePlayer player) {
UpdateLastSeen(player);
}
private void OnPlayerSleepEnded(BasePlayer player) {
DelaySettings.Remove(player);
}
private void OnPlayerDeath(BasePlayer player, HitInfo hitInfo) {
if (player == null || !player.IsHuman()) {
return;
}
if (player.IsConnected) {
UpdateLastSeen(player);
}
var abandonedBuilding = AbandonedBuilding.Get(player, hitInfo);
if (abandonedBuilding == null) {
return;
}
abandonedBuilding.OnPlayerExit(player, true);
}
private void OnEntitySpawned(DroppedItemContainer backpack) {
if (!config.Abandoned.BackpacksLooted) {
return;
}
NextTick(() => {
if (backpack.IsKilled()) {
return;
}
if (DelaySettings.Has(backpack.playerSteamID) || EventTerritory(backpack.transform.position)) {
backpack.playerSteamID = 0;
}
}
);
// ... [Code preview truncated due to length. Full source in attached file] ...