C#:
/*
* <----- 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 © 2021 nivex
*/
using Facepunch;
using Facepunch.Math;
using Network;
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 Oxide.Game.Rust.Libraries;
using Oxide.Plugins.RaidableBasesExtensionMethods;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
namespace Oxide.Plugins {
[Info("Raidable Bases", "nivex", "2.4.1")]
[Description("Create fully automated raidable bases with npcs.")]
class RaidableBases : RustPlugin {
[PluginReference]
private Plugin DangerousTreasures, ZoneManager, IQEconomic, Economics, ServerRewards, GUIAnnouncements, CopyPaste, Friends, Clans, Kits, TruePVE, NightLantern, Wizardry, NextGenPVE, Imperium, Backpacks, BaseRepair, Notify;
private new const string Name = "RaidableBases";
private const int visibleLayer = Layers.Mask.Deployed | Layers.Mask.Default | Layers.Mask.Construction | Layers.Mask.World | Layers.Mask.Terrain;
private const int targetLayer = Layers.Mask.Default | Layers.Mask.Water | Layers.Solid;
private static RaidableBases Instance {
get;
set;
}
protected RotationCycle Cycle {
get;
set;
}
= new RotationCycle();
public Dictionary<int, List<BaseEntity>> Bases {
get;
}
= new Dictionary<int, List<BaseEntity>>();
public Dictionary<int, RaidableBase> Raids {
get;
}
= new Dictionary<int, RaidableBase>();
public Dictionary<ulong, RaidableBase> Npcs {
get;
set;
}
= new Dictionary<ulong, RaidableBase>();
public Dictionary<ulong, DelaySettings> PvpDelay {
get;
}
= new Dictionary<ulong, DelaySettings>();
public Dictionary<ulong, HumanoidBrain> HumanoidBrains {
get;
set;
}
= new Dictionary<ulong, HumanoidBrain>();
private Dictionary<string, SkinInfo> Skins {
get;
}
= new Dictionary<string, SkinInfo>();
protected Dictionary<string, BuyableInfo> buyCooldowns {
get;
set;
}
= new Dictionary<string, BuyableInfo>();
private Dictionary<uint, BMGELEVATOR> _elevators = new Dictionary<uint, BMGELEVATOR>();
public static StoredData data {
get;
set;
}
= new StoredData();
private static StringBuilder _sb {
get;
set;
}
private bool wiped {
get;
set;
}
private bool buyableEnabled {
get;
set;
}
private float lastSpawnRequestTime {
get;
set;
}
private bool IsUnloading {
get;
set;
}
private bool bypassRestarting {
get;
set;
}
private List<string> tryBuyCooldowns {
get;
set;
}
= new List<string>();
private BuildingTables Buildings {
get;
set;
}
private bool DebugMode {
get;
set;
}
private List<RandomBase> Locations {
get;
set;
}
= new List<RandomBase>();
private Dictionary<string, ItemDefinition> _definitions {
get;
set;
}
= new Dictionary<string, ItemDefinition>();
private Dictionary<Vector3, float> LoadingTimes {
get;
set;
}
= new Dictionary<Vector3, float>();
private List<string> Helms {
get;
set;
}
= new List<string> {
"hat.wolf", "attire.hide.helterneck", "hat.beenie", "hat.boonie", "bucket.helmet", "burlap.headwrap", "hat.candle", "hat.cap", "clatter.helmet", "coffeecan.helmet", "deer.skull.mask", "heavy.plate.helmet", "hat.miner", "partyhat", "riot.helmet", "wood.armor.helmet", "mask.balaclava", "mask.bandana", "metal.facemask", "nightvisiongoggles", "hat.dragonmask", "hat.ratmask", "attire.nesthat"
}
;
private List<string> Boots {
get;
set;
}
= new List<string> {
"boots.frog", "shoes.boots", "burlap.shoes", "attire.hide.boots"
}
;
private List<string> Gloves {
get;
set;
}
= new List<string> {
"burlap.gloves.new", "burlap.gloves", "roadsign.gloves", "tactical.gloves"
}
;
private List<string> Vests {
get;
set;
}
= new List<string> {
"bone.armor.suit", "heavy.plate.jacket", "jacket.snow", "jacket", "wood.armor.jacket", "attire.hide.poncho", "metal.plate.torso", "roadsign.jacket"
}
;
private List<string> Legs {
get;
set;
}
= new List<string> {
"burlap.trousers", "heavy.plate.pants", "attire.hide.pants", "pants.shorts", "wood.armor.pants", "pants"
}
;
private List<string> Shirts {
get;
set;
}
= new List<string> {
"hoodie", "burlap.shirt", "shirt.collared", "attire.hide.vest", "shirt.tanktop", "tshirt.long", "tshirt"
}
;
private List<string> Other {
get;
set;
}
= new List<string> {
"movembermoustachecard", "movembermoustache", "sunglasses02black", "sunglasses02camo", "sunglasses02red", "sunglasses03black", "sunglasses03chrome", "sunglasses03gold", "sunglasses", "twitchsunglasses", "gloweyes", "attire.bunnyears"
}
;
private List<string> Furnaces {
get;
set;
}
= new List<string> {
"furnace.large", "furnace", "furnace_static"
}
;
private List<string> BBQs {
get;
set;
}
= new List<string> {
"bbq", "bbq.deployed", "bbq.static"
}
;
private List<string> Lanterns {
get;
set;
}
= new List<string> {
"chineselantern.deployed", "jackolantern.angry", "jackolantern.happy", "lantern.deployed", "tunalight.deployed"
}
;
private List<string> Refineries {
get;
set;
}
= new List<string> {
"refinery_small_deployed", "small_refinery_static"
}
;
private List<uint> ExcludedMounts {
get;
set;
}
= new List<uint> {
3552983236, 4218596772, 1845856065, 1992774774, 629849447, 4267988016, 1418740895, 2493676858, 3814928951, 1980628900, 703403829, 3061223907, 113644298, 3691382632, 3858860623, 286221745, 2230162530, 51176708, 3363531184, 3224878175
}
;
private List<string> Blocks {
get;
set;
}
= new List<string> {
"wall.doorway", "wall", "wall.frame", "wall.half", "wall.low", "wall.window", "foundation.triangle", "foundation", "wall.external.high.wood", "wall.external.high.stone", "wall.external.high.ice", "floor.triangle.frame", "floor.triangle", "floor.frame"
}
;
private List<string> TrueDamage {
get;
set;
}
= new List<string> {
"spikes.floor", "barricade.metal", "barricade.woodwire", "barricade.wood", "wall.external.high.wood", "wall.external.high.stone", "wall.external.high.ice"
}
;
private List<string> arguments {
get;
set;
}
= new List<string> {
"add", "remove", "list", "clean", "easy", "med", "medium", "hard", "expert", "nightmare", "0", "1", "2", "3", "4"
}
;
private List<RaidableMode> RaidableModes {
get;
set;
}
= new List<RaidableMode> {
RaidableMode.Easy, RaidableMode.Medium, RaidableMode.Hard, RaidableMode.Expert, RaidableMode.Nightmare
}
;
private SkinSettingsImportedWorkshop ImportedWorkshopSkins {
get;
set;
}
public const float M_RADIUS = 25f;
public const float CELL_SIZE = M_RADIUS / 2f;
private List<Record> Records = new List<Record> {
new Record("raidablebases.ladder.easy", "raideasy", RaidableMode.Easy), new Record("raidablebases.ladder.medium", "raidmedium", RaidableMode.Medium), new Record("raidablebases.ladder.hard", "raidhard", RaidableMode.Hard), new Record("raidablebases.ladder.expert", "raidexpert", RaidableMode.Expert), new Record("raidablebases.ladder.nightmare", "raidnightmare", RaidableMode.Nightmare), new Record("raidablebases.th", "raidhunter", RaidableMode.Points),
}
;
public enum RaidableType {
None, Manual, Scheduled, Purchased, Maintained, Grid
}
public enum RaidableMode {
Disabled = -1, Easy = 0, Medium = 1, Hard = 2, Expert = 3, Nightmare = 4, Points = 8888, Random = 9999
}
public enum AlliedType {
All, Clan, Friend, Team
}
public enum CacheType {
Close, Delete, Generic, Temporary, Privilege, Submerged
}
public enum ConstructionType {
Barricade, Ladder, Any
}
public enum LootType {
Easy, Medium, Hard, Expert, Nightmare, Default
}
public enum SpawnResult {
Failure, Transfer, Success, Skipped
}
public class StoredData {
public Dictionary<string, Lockout> Lockouts {
get;
}
= new Dictionary<string, Lockout>();
public Dictionary<string, PlayerInfo> Players {
get;
set;
}
= new Dictionary<string, PlayerInfo>();
public Dictionary<string, UI.Info> UI {
get;
set;
}
= new Dictionary<string, UI.Info>();
public string RaidTime {
get;
set;
}
= DateTime.MinValue.ToString();
public int TotalEvents {
get;
set;
}
public StoredData() {
}
public static void Load() {
try {
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch {
}
if (data == null) data = new StoredData();
if (data.UI == null) data.UI = new Dictionary<string, UI.Info>();
if (data.Players == null) data.Players = new Dictionary<string, PlayerInfo>();
}
public void Save() {
data.Lockouts.RemoveAll((userid, lo) => lo.IsExpired());
data.UI.RemoveAll((userid, uii) => uii.All());
data.Players.RemoveAll((userid, playerInfo) => playerInfo.TotalRaids == 0 || playerInfo.IsExpired());
Interface.Oxide.DataFileSystem.WriteObject(Name, this);
}
}
public class RandomBase {
public string BaseName;
public Vector3 Position;
public BaseProfile Profile;
public RaidableType Type;
}
public class BackpackData {
public DroppedItemContainer backpack;
public BasePlayer player;
public ulong userID;
}
public class BuyableInfo {
public float Time;
public Timer Timer;
}
public class DelaySettings {
public RaidableBase RaidableBase;
public Timer Timer;
public bool AllowPVP;
}
public class MountInfo {
public Vector3 position;
public float radius;
public BaseMountable mountable;
}
public class RaiderInfo {
public ulong uid;
public string id;
public string displayName;
public BasePlayer player;
public bool reward = true;
public RaiderInfo(BasePlayer target) {
player = target;
uid = target.userID;
id = target.UserIDString;
displayName = target.displayName;
}
}
public class SkinInfo {
public List<ulong> skins = new List<ulong>();
public List<ulong> workshopSkins = new List<ulong>();
public List<ulong> importedSkins = new List<ulong>();
public List<ulong> allSkins = new List<ulong>();
}
public class Lockout {
public double Easy, Medium, Hard, Expert, Nightmare;
public bool Any() => Easy > 0 || Medium > 0 || Hard > 0 || Expert > 0 || Nightmare > 0;
public bool IsExpired(RaidableMode mode) {
switch (mode) {
case RaidableMode.Easy : return Easy <= 0 || Easy - Epoch.Current <= 0;
case RaidableMode.Medium : return Medium <= 0 || Medium - Epoch.Current <= 0;
case RaidableMode.Hard : return Hard <= 0 || Hard - Epoch.Current <= 0;
case RaidableMode.Expert : return Expert <= 0 || Expert - Epoch.Current <= 0;
case RaidableMode.Nightmare : return Nightmare <= 0 || Nightmare - Epoch.Current <= 0;
default : return true;
}
}
public bool IsExpired() {
var modes = (RaidableMode[])Enum.GetValues(typeof(RaidableMode));
return modes.All(mode => IsExpired(mode));
}
}
public class Record {
public string Permission;
public string Group;
public RaidableMode Mode;
public Record(string permission, string group, RaidableMode mode) {
Permission = permission;
Group = group;
Mode = mode;
}
}
public class Elevation {
public float Min;
public float Max;
}
public class RaidableSpawnLocation {
public List<Vector3> Surroundings = new List<Vector3>();
public Elevation Elevation = new Elevation();
public Vector3 Location = Vector3.zero;
public float WaterHeight;
public float TerrainHeight;
public float SpawnHeight;
public float Radius;
public bool AutoHeight;
public RaidableSpawnLocation(Vector3 Location) {
this.Location = Location;
}
}
public class MonumentInfoEx {
public float size;
public Bounds bounds;
public string translated;
public Transform transform;
public MonumentInfo monument;
public MonumentInfoEx(MonumentInfo monument) {
this.monument = monument;
transform = monument.transform;
translated = Translated();
size = Size();
bounds = new Bounds(monument.Bounds.center, new Vector3(size, size, size));
if (config.Settings.Management.MonumentDistance > 0f) {
bounds.Expand(config.Settings.Management.MonumentDistance);
}
}
public bool IsInBounds(Vector3 target) {
return monument.IsInBounds(target) || new OBB(monument.transform.position, monument.transform.rotation, bounds).Contains(target);
}
private float Size() {
if (string.IsNullOrEmpty(monument.displayPhrase.translated.TrimEnd())) {
return monument.name.Contains("power_sub") ? 50f : monument.name.Contains("cave") ? 75f : monument.name.Contains("OilrigAI") ? 225f : Mathf.Max(monument.Bounds.size.Max(), 75f);
}
switch (monument.displayPhrase.translated.TrimEnd()) {
case "power_sub_small_1" : case "power_sub_small_2" : case "power_sub_big_1" : case "power_sub_big_2" : case "Stone Quarry" : case "Sulfur Quarry" : case "Water Well" : case "Wild Swamp" : case "HQM Quarry" : return 50f;
case "Abandoned Cabins" : case "Abandoned Supermarket" : case "Fishing Village" : case "Large Fishing Village" : case "Lighthouse" : case "Mining Outpost" : case "Ice Lake" : case "The Dome" : case "Oxum's Gas Station" : return 100f;
case "Military Tunnel" : case "Sewer Branch" : case "Satellite Dish" : case "Junkyard" : case "Underwater Lab" : return 125f;
case "Harbor" : case "Train Yard" : case "Power Plant" : return 150f;
case "Barn" : case "Large Barn" : case "Ranch" : return 200f;
case "Airfield" : case "Oil Rig" : case "Large Oil Rig" : case "Water Treatment Plant" : case "Giant Excavator Pit" : return 225f;
case "Bandit Camp" : case "Outpost" : return 350f;
case "Launch Site" : return 300f;
}
return monument.Bounds.size.Max() > 0f ? monument.Bounds.size.Max() : 100f;
}
private string Translated() {
if (!string.IsNullOrEmpty(monument.displayPhrase.translated.TrimEnd())) {
return monument.displayPhrase.translated.TrimEnd();
}
else if (monument.name.Contains("Oilrig")) {
return "Oil Rig";
}
else if (monument.name.Contains("cave")) {
return "Cave";
}
else if (monument.name.Contains("power_sub")) {
return "Power Sub Station";
}
return "Unknown Monument";
}
}
public class ZoneInfo {
public Vector3 Position;
public Vector3 Size;
public float Distance;
public OBB OBB;
public ZoneInfo(object location, object radius, object size) {
Position = (Vector3)location;
if (radius is float) {
Distance = (float)radius + M_RADIUS + config.Settings.ZoneDistance;
}
if (size is Vector3) {
Size = (Vector3)size;
}
var bounds = new Bounds(Position, Size);
if (config.Settings.ZoneDistance > 0f) {
bounds.Expand(config.Settings.ZoneDistance);
}
OBB = new OBB(Position, default(Quaternion), bounds);
}
}
public class BaseProfile {
public List<LootItem> BaseLootList {
get;
set;
}
= new List<LootItem>();
public BuildingOptions Options {
get;
set;
}
= new BuildingOptions();
public RaidableSpawns Spawns {
get;
set;
}
= new RaidableSpawns();
public string Name {
get;
set;
}
public BaseProfile() {
Options.AdditionalBases = new Dictionary<string, List<PasteOption>>();
}
public BaseProfile(BuildingOptions options, string name) {
Options = options;
Name = name;
}
public static BaseProfile Clone(BaseProfile profile) {
return profile.MemberwiseClone() as BaseProfile;
}
}
private class BuildingTables {
public Dictionary<LootType, List<LootItem>> DifficultyLootLists {
get;
set;
}
= new Dictionary<LootType, List<LootItem>>();
public Dictionary<DayOfWeek, List<LootItem>> WeekdayLootLists {
get;
set;
}
= new Dictionary<DayOfWeek, List<LootItem>>();
public Dictionary<string, BaseProfile> Profiles {
get;
set;
}
= new Dictionary<string, BaseProfile>();
}
public class BackboneController : SingletonComponent<BackboneController> {
private RaidableBases Plugin {
get;
set;
}
public Core.Libraries.Lang lang => Plugin.lang;
private StringBuilder sb {
get;
set;
}
= new StringBuilder();
public float OceanLevel {
get;
set;
}
internal ScheduledController Scheduled {
get;
set;
}
internal MaintainedController Maintained {
get;
set;
}
public void Initialize(RaidableBases instance) {
Scheduled = new ScheduledController(instance) {
Enabled = config.Settings.Schedule.Enabled
}
;
Maintained = new MaintainedController(instance) {
Enabled = config.Settings.Maintained.Enabled
}
;
OceanLevel = WaterSystem.OceanLevel;
Plugin = instance;
InvokeRepeating(CheckOceanLevel, 60f, 60f);
}
private void CheckOceanLevel() {
if (OceanLevel != WaterSystem.OceanLevel) {
OceanLevel = WaterSystem.OceanLevel;
RaidableSpawns spawns;
if (GridController.Spawns.TryGetValue(RaidableType.Grid, out spawns)) {
spawns.TryAddRange(CacheType.Submerged);
}
}
}
public void Message(BasePlayer player, string key, params object[] args) {
if (player.IsValid()) {
Plugin.Player.Message(player, GetMessage(key, player.UserIDString, args), config.Settings.ChatID);
}
}
public string GetMessage(string key, string id = null, params object[] args) {
sb.Length = 0;
if (config.EventMessages.Prefix) {
sb.Append(lang.GetMessage("Prefix", Plugin, id));
}
sb.Append(id == "server_console" || id == null ? RemoveFormatting(lang.GetMessage(key, Plugin, id)) : lang.GetMessage(key, Plugin, id));
return args.Length > 0 ? string.Format(sb.ToString(), args) : sb.ToString();
}
public string GetMessageEx(string key, string id = null, params object[] args) {
sb.Length = 0;
sb.Append(id == "server_console" || id == null ? RemoveFormatting(lang.GetMessage(key, Plugin, id)) : lang.GetMessage(key, Plugin, id));
return args.Length > 0 ? string.Format(sb.ToString(), args) : sb.ToString();
}
public string RemoveFormatting(string source) => source.Contains(">") ? Regex.Replace(source, "<.*?>", string.Empty) : source;
public void StopCoroutines() {
Scheduled.StopCoroutine();
Maintained.StopCoroutine();
GridController.StopCoroutine();
GarbageController.StopCoroutine();
}
public double GetRaidTime() => DateTime.Parse(data.RaidTime).Subtract(DateTime.Now).TotalSeconds;
public void StartAutomation() {
if (Scheduled.Enabled) {
if (data.RaidTime != DateTime.MinValue.ToString() && GetRaidTime() > config.Settings.Schedule.IntervalMax) {
data.RaidTime = DateTime.MinValue.ToString();
}
Scheduled.StartCoroutine();
}
if (Maintained.Enabled) {
Maintained.StartCoroutine();
}
}
}
public class UndoSettings {
public List<BaseEntity> Entities;
public object[] hookObjects;
public bool Structures;
public bool Deployables;
public bool Mounts;
public bool Teleport;
public int Limit;
}
public static class GarbageController {
internal static Coroutine despawnCoroutine {
get;
set;
}
internal static Dictionary<BaseEntity, MountInfo> Mounts {
get;
set;
}
= new Dictionary<BaseEntity, MountInfo>();
internal static Dictionary<BaseEntity, RaidableBase> RaidEntities {
get;
set;
}
= new Dictionary<BaseEntity, RaidableBase>();
public static void UndoLoop(UndoSettings undo, int count = 0) {
undo.Entities.RemoveAll(e => e.IsKilled());
undo.Entities.Sort((x, y) => (x is BuildingBlock).CompareTo(y is BuildingBlock));
undo.Entities.Take(undo.Limit).ToList().ForEach(entity => {
undo.Entities.Remove(entity);
if (entity.IsKilled() || KeepEntity(entity, undo)) {
return;
}
if (entity is IOEntity) {
var io = entity as IOEntity;
io.ClearConnections();
if (entity is SamSite) {
var ss = entity as SamSite;
ss.staticRespawn = false;
}
}
if (undo.Teleport) {
entity.transform.position = new Vector3(0f, -500f, 0f);
Interface.Oxide.NextTick(entity.SafelyKill);
}
else entity.Kill(BaseNetworkable.DestroyMode.None);
}
);
if (count != 0 && undo.Entities.Count != 0 && undo.Entities.Count == count) {
goto done;
}
if (undo.Entities.Count > 0) {
Interface.Oxide.NextTick(() => UndoLoop(undo, undo.Entities.Count));
return;
}
done : GarbageController.RaidEntities.RemoveAll((e, raid) => e.IsKilled());
if (GarbageController.RaidEntities.Count == 0) {
GarbageController.Mounts.Clear();
}
if (undo.hookObjects != null) {
Interface.CallHook("OnRaidableBaseDespawned", undo.hookObjects);
}
}
private static bool KeepEntity(BaseEntity entity, UndoSettings undo) {
if (!undo.Mounts && KeepMountable(entity) || entity.OwnerID.IsSteamId() && KeepPlayerEntity(entity, undo)) {
return true;
}
RaidEntities.Remove(entity);
return false;
}
private static bool KeepPlayerEntity(BaseEntity entity, UndoSettings undo) {
if (entity.PrefabName.Contains("assets/prefabs/deployable/")) {
if (!undo.Deployables) {
if (entity is IItemContainerEntity) {
var ice = entity as IItemContainerEntity;
if (ice != null) {
DropUtil.DropItems(ice.inventory, entity.transform.position + Vector3.up);
}
}
return false;
}
}
else if (!undo.Structures) {
return false;
}
RaidEntities.Remove(entity);
return true;
}
private static bool KeepMountable(BaseEntity entity) {
MountInfo mi;
if (!Mounts.TryGetValue(entity, out mi) || !Mounts.Remove(entity)) {
return false;
}
return mi.mountable.GetMounted() != null || !InRange(entity.transform.position, mi.position, mi.radius);
}
public static void RemoveHeldEntities() {
foreach (var element in RaidEntities.ToList()) {
if (element.Key is IItemContainerEntity) {
var ice = element.Key as IItemContainerEntity;
if (ice == null || ice.inventory == null) {
continue;
}
foreach (Item item in ice.inventory.itemList) {
var e = item.GetHeldEntity();
if (e.IsValid()) {
e.enableSaving = false;
BaseEntity.saveList.Remove(e);
}
}
ice.inventory.Clear();
}
}
ItemManager.DoRemoves();
}
public static void StopCoroutine() {
if (despawnCoroutine != null) {
ServerMgr.Instance.StopCoroutine(despawnCoroutine);
despawnCoroutine = null;
}
}
public static void StartDespawn(bool inactiveOnly) {
if (Instance.Raids.Count == 0) {
return;
}
if (despawnCoroutine != null) {
Instance.timer.Once(0.1f, () => StartDespawn(inactiveOnly));
return;
}
despawnCoroutine = ServerMgr.Instance.StartCoroutine(DespawnCoroutine(inactiveOnly));
}
private static IEnumerator DespawnCoroutine(bool inactiveOnly) {
foreach (var raid in Instance.Raids.Values.ToList()) {
if (raid == null || inactiveOnly && (raid.intruders.Count > 0 || raid.ownerId.IsSteamId())) {
continue;
}
var baseIndex = raid.BaseIndex;
Instance.Raids.Remove(raid.uid);
raid.Despawn();
yield return new WaitWhile(() => Instance.Bases.ContainsKey(baseIndex));
yield return CoroutineEx.waitForSeconds(0.1f);
}
despawnCoroutine = null;
}
}
public class EjectController : FacepunchBehaviour {
private static EjectController _ejectController;
private GameObject _gameObject;
private Vector3 position;
private float radius;
private void OnDestroy() {
_ejectController = null;
}
public static void DestroyMe() {
if (_ejectController == null) {
return;
}
DestroyImmediate(_ejectController._gameObject);
}
public static void Initialize(Vector3 position, float radius) {
GameObject go;
if (_ejectController == null) {
go = new GameObject("EjectController");
_ejectController = go.AddComponent<EjectController>();
_ejectController._gameObject = go;
}
else go = _ejectController._gameObject;
go.transform.position = _ejectController.position = position;
go.layer = (int)Layer.Trigger;
var collider = go.AddComponent<SphereCollider>();
collider.isTrigger = true;
collider.radius = _ejectController.radius = radius;
collider.center = Vector3.zero;
}
private void OnTriggerEnter(Collider collider) {
if (collider == null || collider.name == "ZoneManager") {
return;
}
var entity = GetEntity(collider);
if (entity is BasePlayer) {
var player = entity as BasePlayer;
if (CanBypass(player) || IsUnderground(player.transform.position)) {
return;
}
RaidableBase.RemovePlayer(player, 7, position, radius, RaidableType.Manual);
}
else if (entity is BaseMountable) {
var m = entity as BaseMountable;
var players = RaidableBase.GetMountedPlayers(m);
if (players.Count == 0 || players.Exists(player => CanBypass(player))) {
return;
}
RaidableBase.RemoveMountable(m, players, position, radius);
}
}
private bool CanBypass(BasePlayer player) {
return !player.IsHuman() || player.IsFlying || player.limitNetworking || player.HasPermission("raidablebases.canbypass");
}
}
public static class GridController {
internal static HashSet<RaidableSpawnLocation> Seabed {
get;
set;
}
internal static Dictionary<RaidableType, RaidableSpawns> Spawns {
get;
set;
}
internal static Coroutine gridCoroutine {
get;
set;
}
internal static float gridTime {
get;
set;
}
public static void Initialize() {
Seabed = new HashSet<RaidableSpawnLocation>();
Spawns = new Dictionary<RaidableType, RaidableSpawns>();
}
public static void Clear() {
Seabed.Clear();
Spawns.Clear();
}
public static void Setup() {
if (Spawns.Count >= 5) {
Instance.timer.Once(5f, () => BackboneController.Instance.StartAutomation());
return;
}
StopCoroutine();
Interface.Oxide.NextTick(() => gridCoroutine = ServerMgr.Instance.StartCoroutine(GenerateGrid()));
}
public static void StopCoroutine() {
if (gridCoroutine != null) {
ServerMgr.Instance.StopCoroutine(gridCoroutine);
gridCoroutine = null;
}
}
private static IEnumerator GenerateGrid() {
var gridStopwatch = new Stopwatch();
gridStopwatch.Start();
RaidableSpawns spawns = Spawns[RaidableType.Grid] = new RaidableSpawns();
gridTime = Time.realtimeSinceStartup;
float waterDepthMax = Instance.Buildings.Profiles.Exists(x => x.Value.Options.Water.AllowSubmerged) ? 3f : 0f;
float monumentDist = M_RADIUS * 2f + config.Settings.Management.MonumentDistance;
bool seabed = Instance.Buildings.Profiles.Exists(x => x.Value.Options.Water.Seabed > 0f);
int minPos = (int)(World.Size / -2f);
int maxPos = (int)(World.Size / 2f);
float protectionRadius = 50f;
float elevation = 0.5f;
int checks = 0;
foreach (var profile in Instance.Buildings.Profiles.Values) {
protectionRadius = Mathf.Max(profile.Options.ProtectionRadii.Max(), profile.Options.ArenaWalls.Radius, protectionRadius);
waterDepthMax = Mathf.Max(profile.Options.Water.WaterDepth, seabed ? waterDepthMax : 40f);
elevation = Mathf.Max(profile.Options.Elevation, elevation);
}
for (float x = minPos; x < maxPos; x += CELL_SIZE) // Credits to Jake_Rich for creating this for me!
{
for (float z = minPos; z < maxPos; z += CELL_SIZE) {
var position = new Vector3(x, 0f, z);
position.y = SpawnsController.GetSpawnHeight(position);
SpawnsController.ExtractLocation(spawns, position, elevation, monumentDist, protectionRadius, waterDepthMax, seabed);
if (++checks >= 75) {
checks = 0;
yield return CoroutineEx.waitForSeconds(0.025f);
}
}
}
gridCoroutine = null;
gridStopwatch.Stop();
BackboneController.Instance.StartAutomation();
Puts(BackboneController.Instance.GetMessageEx("InitializedGrid", null, gridStopwatch.Elapsed.Seconds, gridStopwatch.Elapsed.Milliseconds, World.Size, spawns.Count));
if (GridController.Seabed.Count > 0) Puts(BackboneController.Instance.GetMessageEx("InitializedGridSea", null, GridController.Seabed.Count));
}
public static void LoadSpawns() {
Spawns.Clear();
Spawns.Add(RaidableType.Grid, new RaidableSpawns());
if (SpawnsFileValid(config.Settings.Manual.SpawnsFile)) {
var spawns = GetSpawnsLocations(config.Settings.Manual.SpawnsFile);
if (spawns.Count > 0) {
Puts(BackboneController.Instance.GetMessageEx("LoadedManual", null, spawns.Count));
Spawns[RaidableType.Manual] = new RaidableSpawns(spawns);
}
}
if (SpawnsFileValid(config.Settings.Schedule.SpawnsFile)) {
var spawns = GetSpawnsLocations(config.Settings.Schedule.SpawnsFile);
if (spawns?.Count > 0) {
Puts(BackboneController.Instance.GetMessageEx("LoadedScheduled", null, spawns.Count));
Spawns[RaidableType.Scheduled] = new RaidableSpawns(spawns);
}
}
if (SpawnsFileValid(config.Settings.Maintained.SpawnsFile)) {
var spawns = GetSpawnsLocations(config.Settings.Maintained.SpawnsFile);
if (spawns?.Count > 0) {
Puts(BackboneController.Instance.GetMessageEx("LoadedMaintained", null, spawns.Count));
Spawns[RaidableType.Maintained] = new RaidableSpawns(spawns);
}
}
if (SpawnsFileValid(config.Settings.Buyable.SpawnsFile)) {
var spawns = GetSpawnsLocations(config.Settings.Buyable.SpawnsFile);
if (spawns?.Count > 0) {
Puts(BackboneController.Instance.GetMessageEx("LoadedBuyable", null, spawns.Count));
Spawns[RaidableType.Purchased] = new RaidableSpawns(spawns);
}
}
}
public static bool SpawnsFileValid(string spawnsFile) {
if (string.IsNullOrEmpty(spawnsFile) || spawnsFile.Equals("none", StringComparison.OrdinalIgnoreCase)) {
return false;
}
return FileExists($"SpawnsDatabase{Path.DirectorySeparatorChar}{spawnsFile}");
}
public static HashSet<RaidableSpawnLocation> GetSpawnsLocations(string spawnsFile) {
Spawnfile data;
try {
data = Interface.Oxide.DataFileSystem.ReadObject<Spawnfile>($"SpawnsDatabase{Path.DirectorySeparatorChar}{spawnsFile}");
}
catch (JsonReaderException) {
return null;
}
var locations = new HashSet<RaidableSpawnLocation>();
if (data == null) {
return locations;
}
foreach (var element in data.spawnPoints) {
if (element.Value == null) continue;
var value = element.Value.ToString();
if (string.IsNullOrEmpty(value)) continue;
var vector = value.ToVector3();
if (vector == Vector3.zero) continue;
locations.Add(new RaidableSpawnLocation(vector));
}
return locations;
}
}
private class Spawnfile {
public Dictionary<string, object> spawnPoints = new Dictionary<string, object>();
}
public class MaintainedController {
internal Coroutine Coroutine {
get;
set;
}
internal bool Enabled {
get;
set;
}
internal RaidableBases Instance {
get;
set;
}
public MaintainedController(RaidableBases instance) {
Instance = instance;
}
public void StopCoroutine() {
if (Coroutine != null) {
ServerMgr.Instance.StopCoroutine(Coroutine);
Coroutine = null;
}
}
public void StartCoroutine() {
if (!Enabled || config.Settings.Maintained.Max <= 0) {
return;
}
if (Instance.IsGridLoading) {
Instance.timer.Once(1f, () => StartCoroutine());
return;
}
StopCoroutine();
if (!Instance.CanContinueAutomation()) {
Puts(BackboneController.Instance.GetMessageEx("MaintainCoroutineFailedToday"));
return;
}
Instance.timer.Once(0.2f, () => Coroutine = ServerMgr.Instance.StartCoroutine(MaintainCoroutine()));
}
private IEnumerator MaintainCoroutine() {
string message;
RaidableMode mode;
RandomBase randomBase;
while (true) {
if (Enabled && CanMaintainOpenEvent()) {
if (!Instance.IsCopyPasteLoaded(null)) {
yield return CoroutineEx.waitForSeconds(60f);
}
else if (SaveRestore.IsSaving) {
PrintDebugMessage("Maintained: Server saving");
yield return CoroutineEx.waitForSeconds(15f);
}
else if (!IsModeValid(mode = Instance.GetRandomDifficulty(RaidableType.Maintained))) {
PrintDebugMessage($"Maintained: Invalid mode {mode}");
yield return CoroutineEx.waitForSeconds(1f);
}
else if ((randomBase = Instance.SpawnRandomBase(out message, RaidableType.Maintained, mode)) != null) {
RaidableBase.IsSpawning = true;
PrintDebugMessage($"Maintained: Waiting for base at {randomBase.Position} to be setup by the plugin.");
yield return new WaitWhile(() => RaidableBase.IsSpawning);
PrintDebugMessage($"Maintained: Base has been setup by the plugin.");
if (config.Settings.Maintained.Time > 0) {
PrintDebugMessage($"Maintained: Waiting {config.Settings.Maintained.Time} seconds.");
yield return CoroutineEx.waitForSeconds(config.Settings.Maintained.Time);
}
}
else {
if (string.IsNullOrEmpty(message)) {
message = "Maintained: Failed to find position; trying again...";
}
PrintDebugMessage(message);
}
}
if (Enabled) PrintDebugMessage("Maintained coroutine is waiting for 1 second.");
yield return CoroutineEx.waitForSeconds(1f);
}
}
private bool CanMaintainOpenEvent() {
if (Instance.Raids.Values.Exists(raid => raid.IsDespawning)) {
var vector = Instance.Raids.Values.FirstOrDefault(raid => raid.IsDespawning).Location;
PrintDebugMessage($"Maintained: Paste not available; a base is currently despawning at {vector}");
return false;
}
if (!Instance.IsPasteAvailable) {
var vector = Instance.Raids.Values.FirstOrDefault(raid => raid.IsLoading)?.Location;
PrintDebugMessage($"Maintained: Paste not available; a base is currently loading at {vector}");
return false;
}
if (RaidableBase.IsSpawning) {
PrintDebugMessage($"Maintained: Paste not available; a base is currently spawning.");
return false;
}
if (Instance.IsGridLoading) {
PrintDebugMessage($"Maintained: Grid is loading.");
re
// ... [Code preview truncated due to length. Full source in attached file] ...