C#:
#define USE_HTN_HOOK
//#define DEBUG_DRAWINGS
//#define DEBUG_ROCKS
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 Rust;
using Rust.Ai;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
using static NPCPlayerApex;
namespace Oxide.Plugins {
[Info("Raidable Bases", "nivex", "1.7.1")]
[Description("Create fully automated raidable bases with npcs.")]
class RaidableBases : RustPlugin {
[PluginReference]
private Plugin DangerousTreasures, Vanish, LustyMap, ZoneManager, Economics, ServerRewards, Map, GUIAnnouncements, CopyPaste, Friends, Clans, Kits, TruePVE, Spawns, NightLantern, Wizardry, NextGenPVE, Imperium, Titan, Backpacks;
private void OnPluginLoaded(Plugin plugin) {
if (plugin.Name == "Titan") {
Titan = plugin;
Unsubscribe(nameof(OnNpcTarget));
Unsubscribe(nameof(OnPlayerDropActiveItem));
}
}
protected static SingletonBackbone Backbone {
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<BaseEntity, RaidableBase> RaidEntities {
get;
}
= new Dictionary<BaseEntity, RaidableBase>();
public Dictionary<int, RaidableBase> Indices {
get;
set;
}
= new Dictionary<int, RaidableBase>();
public Dictionary<ulong, RaidableBase> Npcs {
get;
set;
}
= new Dictionary<ulong, RaidableBase>();
protected Dictionary<ulong, DelaySettings> PvpDelay {
get;
}
= new Dictionary<ulong, DelaySettings>();
private Dictionary<string, SkinInfo> Skins {
get;
}
= new Dictionary<string, SkinInfo>();
private Dictionary<MonumentInfo, float> monuments {
get;
set;
}
= new Dictionary<MonumentInfo, float>();
private Dictionary<Vector3, ZoneInfo> managedZones {
get;
set;
}
= new Dictionary<Vector3, ZoneInfo>();
private Dictionary<int, MapInfo> mapMarkers {
get;
set;
}
= new Dictionary<int, MapInfo>();
private Dictionary<int, string> lustyMarkers {
get;
set;
}
= new Dictionary<int, string>();
protected Dictionary<RaidableType, RaidableSpawns> raidSpawns {
get;
set;
}
= new Dictionary<RaidableType, RaidableSpawns>();
protected Dictionary<string, float> buyCooldowns {
get;
set;
}
= new Dictionary<string, float>();
protected Dictionary<uint, AutoTurret> ElectricalConnections {
get;
set;
}
= new Dictionary<uint, AutoTurret>();
public StoredData storedData {
get;
set;
}
= new StoredData();
protected Coroutine despawnCoroutine {
get;
set;
}
protected Coroutine maintainCoroutine {
get;
set;
}
protected Coroutine scheduleCoroutine {
get;
set;
}
protected Coroutine gridCoroutine {
get;
set;
}
private Stopwatch gridStopwatch {
get;
}
= new Stopwatch();
private StringBuilder _sb {
get;
}
= new StringBuilder();
protected const float Radius = 25f;
private bool wiped {
get;
set;
}
private float lastSpawnRequestTime {
get;
set;
}
private float gridTime {
get;
set;
}
private bool IsUnloading {
get;
set;
}
private int _maxOnce {
get;
set;
}
private List<string> tryBuyCooldowns {
get;
set;
}
= new List<string>();
private static BuildingTables Buildings {
get;
set;
}
= new BuildingTables();
private const uint LARGE_WOODEN_BOX = 2206646561;
private const uint SMALL_WOODEN_BOX = 1560881570;
private const uint COFFIN_STORAGE = 4080262419;
private const float INSTRUCTION_TIME = 0.02f;
private bool maintainEnabled {
get;
set;
}
private bool scheduleEnabled {
get;
set;
}
private bool buyableEnabled {
get;
set;
}
private static List<ulong> BoxSkins {
get;
set;
}
= new List<ulong>();
private static Dictionary<string, ItemDefinition> shortnameDefs = new Dictionary<string, ItemDefinition>();
private static List<string> shortnames = new List<string>();
private static Dictionary<Vector3, float> LoadingTimes = new Dictionary<Vector3, float>();
private static bool IsBox(uint prefabID) => prefabID == LARGE_WOODEN_BOX || prefabID == SMALL_WOODEN_BOX || prefabID == COFFIN_STORAGE;
private List<string> Helms = 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 = new List<string> {
"boots.frog", "shoes.boots", "burlap.shoes", "attire.hide.boots"
}
;
private List<string> Gloves = new List<string> {
"burlap.gloves.new", "burlap.gloves", "roadsign.gloves", "tactical.gloves"
}
;
private List<string> Vests = 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 = new List<string> {
"burlap.trousers", "heavy.plate.pants", "attire.hide.pants", "pants.shorts", "wood.armor.pants", "pants"
}
;
private List<string> Shirts = new List<string> {
"hoodie", "burlap.shirt", "shirt.collared", "attire.hide.vest", "shirt.tanktop", "tshirt.long", "tshirt"
}
;
private List<string> Other = new List<string> {
"movembermoustachecard", "movembermoustache", "sunglasses02black", "sunglasses02camo", "sunglasses02red", "sunglasses03black", "sunglasses03chrome", "sunglasses03gold", "sunglasses", "twitchsunglasses", "gloweyes", "attire.bunnyears"
}
;
private List<uint> Furnaces = new List<uint> {
1374462671, 2931042549, 1402456403
}
;
private List<uint> BBQs = new List<uint> {
2409469892, 128449714
}
;
private List<uint> Lanterns = new List<uint> {
3887352222, 1889323056, 630866573, 4027991414, 1392608348
}
;
private List<uint> Refineries = new List<uint> {
1057236622, 919097516
}
;
//campfire: 4160694184, campfire_static: 1339281147, cursedcauldron.deployed: 1348425051, fireplace.deployed: 110576239, hobobarrel_static: 754638672, skull_fire_pit: 1906669538
private enum SpawnResult {
Failure, Transfer, Success, Skipped
}
private enum LootType {
Easy, Medium, Hard, Expert, Nightmare, Default
}
public class ZoneInfo {
public Vector3 Position;
public Vector3 Size;
public float Distance;
public OBB OBB;
}
public class SkinInfo {
public List<ulong> skins = new List<ulong>();
public List<ulong> allSkins = new List<ulong>();
}
public class BaseProfile {
public List<TreasureItem> BaseLootList {
get;
set;
}
= new List<TreasureItem>();
public BuildingOptions Options {
get;
set;
}
= new BuildingOptions();
public BaseProfile() {
}
public BaseProfile(BuildingOptions options) {
Options = options;
}
public static BaseProfile Clone(BaseProfile profile) {
return profile.MemberwiseClone() as BaseProfile;
}
}
private class BuildingTables {
public Dictionary<LootType, List<TreasureItem>> DifficultyLootLists {
get;
set;
}
= new Dictionary<LootType, List<TreasureItem>>();
public Dictionary<DayOfWeek, List<TreasureItem>> WeekdayLootLists {
get;
set;
}
= new Dictionary<DayOfWeek, List<TreasureItem>>();
public Dictionary<string, BaseProfile> Profiles {
get;
set;
}
= new Dictionary<string, BaseProfile>();
public bool ContainsKey(string fileName) {
if (string.IsNullOrEmpty(fileName)) {
return true;
}
foreach (var profile in Profiles.Values) {
if (profile.Options.AdditionalBases.ContainsKey(fileName)) {
return true;
}
}
return Profiles.ContainsKey(fileName);
}
}
private enum OperationalState {
On, Off, Unknown
}
public class DelaySettings {
public RaidableBase RaidableBase;
public Timer Timer;
public bool AllowPVP;
}
public enum RaidableType {
None, Manual, Scheduled, Purchased, Maintained, Grid
}
public enum RaidableMode {
Disabled = -1, Easy = 0, Medium = 1, Hard = 2, Expert = 3, Nightmare = 4, Random = 9999
}
public class ResourcePath {
public List<uint> Blocks {
get;
}
public List<uint> TrueDamage {
get;
}
public ItemDefinition BoxDefinition {
get;
}
public string ExplosionMarker {
get;
}
public string CodeLock {
get;
}
public string FireballSmall {
get;
}
public string Fireball {
get;
}
public string HighExternalWoodenWall {
get;
}
public string HighExternalStoneWall {
get;
}
public string Ladder {
get;
}
public string Murderer {
get;
}
public string RadiusMarker {
get;
}
public string Sphere {
get;
}
public string Scientist {
get;
}
public string VendingMarker {
get;
}
public ResourcePath() {
Blocks = new List<uint> {
803699375, 2194854973, 919059809, 3531096400, 310235277, 2326657495, 3234260181, 72949757, 1745077396, 1585379529
}
;
TrueDamage = new List<uint> {
976279966, 3824663394, 1202834203, 4254045167, 1745077396, 1585379529
}
;
BoxDefinition = ItemManager.FindItemDefinition(StringPool.Get(2735448871));
CodeLock = StringPool.Get(3518824735);
ExplosionMarker = StringPool.Get(4060989661);
FireballSmall = StringPool.Get(2086405370);
Fireball = StringPool.Get(3369311876);
HighExternalWoodenWall = StringPool.Get(1745077396);
HighExternalStoneWall = StringPool.Get(1585379529);
Ladder = StringPool.Get(2150203378);
Murderer = StringPool.Get(3879041546);
RadiusMarker = StringPool.Get(2849728229);
Sphere = StringPool.Get(3211242734);
Scientist = StringPool.Get(4223875851);
VendingMarker = StringPool.Get(3459945130);
}
}
public class SingletonBackbone : SingletonComponent<SingletonBackbone> {
public RaidableBases Plugin {
get;
private set;
}
public ResourcePath Path {
get;
private set;
}
public Oxide.Core.Libraries.Lang lang => Plugin.lang;
private StringBuilder sb => Plugin._sb;
public StoredData Data => Plugin.storedData;
public Dictionary<ulong, FinalDestination> Destinations {
get;
set;
}
public string Easy {
get;
set;
}
public string Medium {
get;
set;
}
public string Hard {
get;
set;
}
public string Expert {
get;
set;
}
public string Nightmare {
get;
set;
}
public float OceanLevel {
get;
set;
}
public SingletonBackbone(RaidableBases plugin) {
Plugin = plugin;
Path = new ResourcePath();
Destinations = new Dictionary<ulong, FinalDestination>();
Easy = RemoveFormatting(GetMessage("ModeEasy")).ToLower();
Medium = RemoveFormatting(GetMessage("ModeMedium")).ToLower();
Hard = RemoveFormatting(GetMessage("ModeHard")).ToLower();
Expert = RemoveFormatting(GetMessage("ModeExpert")).ToLower();
Nightmare = RemoveFormatting(GetMessage("ModeNightmare")).ToLower();
OceanLevel = WaterSystem.OceanLevel;
InvokeRepeating(CheckOceanLevel, 60f, 60f);
}
public void Destroy() {
BoxSkins.Clear();
Path = null;
Plugin = null;
DestroyImmediate(Instance);
}
private void CheckOceanLevel() {
if (OceanLevel != WaterSystem.OceanLevel) {
OceanLevel = WaterSystem.OceanLevel;
RaidableSpawns rs;
if (!Plugin.raidSpawns.TryGetValue(RaidableType.Grid, out rs)) {
return;
}
rs.TryAddRange(CacheType.Submerged);
}
}
public void InitializeSkins() {
foreach (var def in ItemManager.GetItemDefinitions()) {
if (!shortnames.Contains(def.shortname)) shortnames.Add(def.shortname);
var imd = def.GetComponent<ItemModDeployable>();
if (imd == null || shortnameDefs.ContainsKey(imd.entityPrefab.resourcePath)) continue;
shortnameDefs.Add(imd.entityPrefab.resourcePath, def);
}
if (Path.BoxDefinition == null) {
return;
}
BoxSkins.Clear();
var skinInfo = RaidableBase.GetItemSkins(Path.BoxDefinition);
if (_config.Skins.Boxes.RandomWorkshopSkins && skinInfo.allSkins.Count > 0) {
BoxSkins.AddRange(skinInfo.allSkins);
}
else if (skinInfo.skins.Count > 0) {
BoxSkins.AddRange(skinInfo.skins);
}
}
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 && id != null && id != "server_console" && !key.EndsWith("Flag")) {
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 RemoveFormatting(string source) => source.Contains(">") ? Regex.Replace(source, "<.*?>", string.Empty) : source;
public Timer Timer(float seconds, Action action) => Plugin.timer.Once(seconds, action);
public bool HasPermission(string id, string perm) => Plugin.permission.UserHasPermission(id, perm);
}
public class Elevation {
public float Min {
get;
set;
}
public float Max {
get;
set;
}
}
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 enum CacheType {
Generic, Construction, Privilege, Submerged,
}
public class RaidableSpawns {
public readonly HashSet<RaidableSpawnLocation> Spawns = new HashSet<RaidableSpawnLocation>();
private readonly Dictionary<CacheType, HashSet<RaidableSpawnLocation>> Cached = new Dictionary<CacheType, HashSet<RaidableSpawnLocation>>();
public int Count {
get {
return Spawns.Count;
}
}
public void Add(RaidableSpawnLocation rsl, CacheType cacheType) {
if (cacheType == CacheType.Submerged) {
if (Backbone.Plugin.IsSubmerged(rsl)) {
Get(cacheType).Add(rsl);
return;
}
}
else if (cacheType == CacheType.Generic) {
if (Backbone.Plugin.EventTerritory(rsl.Location)) {
return;
}
}
Spawns.Add(rsl);
}
public void TryAddRange(CacheType cacheType = CacheType.Generic) {
HashSet<RaidableSpawnLocation> cache = Get(cacheType);
foreach (var rsl in cache) {
Add(rsl, cacheType);
}
cache.RemoveWhere(rsl => Spawns.Contains(rsl));
}
public HashSet<RaidableSpawnLocation> Active => Spawns;
public HashSet<RaidableSpawnLocation> Inactive(CacheType cacheType) => Get(cacheType);
public void Check() {
if (Spawns.Count == 0) {
TryAddRange();
}
}
public RaidableSpawnLocation GetRandom() {
var rsl = Spawns.ElementAt(UnityEngine.Random.Range(0, Spawns.Count));
Remove(rsl, CacheType.Generic);
return rsl;
}
public HashSet<RaidableSpawnLocation> Get(CacheType cacheType) {
HashSet<RaidableSpawnLocation> cache;
if (!Cached.TryGetValue(cacheType, out cache)) {
Cached[cacheType] = cache = new HashSet<RaidableSpawnLocation>();
}
return cache;
}
public void AddNear(Vector3 target, float radius, CacheType cacheType) {
HashSet<RaidableSpawnLocation> cache = Get(cacheType);
AddNear(cache, target, radius);
}
public void AddNear(HashSet<RaidableSpawnLocation> cache, Vector3 target, float radius) {
foreach (var b in cache) {
if (InRange(target, b.Location, radius)) {
Spawns.Add(b);
}
}
cache.RemoveWhere(x => Spawns.Contains(x));
}
public void Remove(RaidableSpawnLocation a, CacheType cacheType) {
HashSet<RaidableSpawnLocation> cache = Get(cacheType);
cache.Add(a);
Spawns.Remove(a);
}
public static float GetDistance(RaidableType type) {
float distance;
switch (type) {
case RaidableType.Maintained : {
distance = _config.Settings.Maintained.Distance;
break;
}
case RaidableType.Purchased : {
distance = _config.Settings.Buyable.Distance;
break;
}
case RaidableType.Scheduled : {
distance = _config.Settings.Schedule.Distance;
break;
}
case RaidableType.Manual : {
distance = _config.Settings.Manual.Distance;
break;
}
default : {
distance = 100f;
break;
}
}
return distance;
}
public float RemoveNear(Vector3 target, float radius, CacheType cacheType, RaidableType type) {
if (cacheType == CacheType.Generic) {
float r = GetDistance(type);
if (r > radius) {
radius = r;
}
}
var cache = Get(cacheType);
foreach (var b in Spawns) {
if (InRange(target, b.Location, radius)) {
cache.Add(b);
}
}
Spawns.RemoveWhere(x => cache.Contains(x));
return radius;
}
public RaidableSpawns(HashSet<RaidableSpawnLocation> spawns) {
Spawns = spawns;
}
public RaidableSpawns() {
}
}
private class MapInfo {
public string Url;
public string IconName;
public Vector3 Position;
}
public class PlayerInfo {
public int TotalRaids {
get;
set;
}
public int Raids {
get;
set;
}
public PlayerInfo() {
}
}
public class Lockout {
public double Easy {
get;
set;
}
public double Medium {
get;
set;
}
public double Hard {
get;
set;
}
public double Expert {
get;
set;
}
public double Nightmare {
get;
set;
}
public bool Any() => Easy > 0 || Medium > 0 || Hard > 0 || Expert > 0 || Nightmare > 0;
}
public class RotationCycle {
private Dictionary<RaidableMode, List<string>> _buildings = new Dictionary<RaidableMode, List<string>>();
public void Add(RaidableType type, RaidableMode mode, string key) {
if (!_config.Settings.Management.RequireAllSpawned || type == RaidableType.Grid || type == RaidableType.Manual) {
return;
}
List<string> keyList;
if (!_buildings.TryGetValue(mode, out keyList)) {
_buildings[mode] = keyList = new List<string>();
}
if (!keyList.Contains(key)) {
keyList.Add(key);
}
}
public bool CanSpawn(RaidableType type, RaidableMode mode, string key) {
if (mode == RaidableMode.Disabled) {
return false;
}
if (!_config.Settings.Management.RequireAllSpawned || mode == RaidableMode.Random || type == RaidableType.Grid || type == RaidableType.Manual) {
return true;
}
List<string> keyList;
if (!_buildings.TryGetValue(mode, out keyList)) {
return true;
}
return TryClear(type, mode, keyList) || !keyList.Contains(key);
}
public bool TryClear(RaidableType type, RaidableMode mode, List<string> keyList) {
foreach (var profile in Buildings.Profiles) {
if (profile.Value.Options.Mode != mode || !CanSpawnDifficultyToday(mode) || MustExclude(type, profile.Value.Options.AllowPVP)) {
continue;
}
if (!keyList.Contains(profile.Key) && FileExists(profile.Key)) {
return false;
}
foreach (var kvp in profile.Value.Options.AdditionalBases) {
if (!keyList.Contains(kvp.Key) && FileExists(kvp.Key)) {
return false;
}
}
}
keyList.Clear();
return true;
}
}
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() {
}
}
private class PlayWithFire : FacepunchBehaviour {
private FireBall fireball {
get;
set;
}
private BaseEntity target {
get;
set;
}
private bool fireFlung {
get;
set;
}
private Coroutine mcCoroutine {
get;
set;
}
public BaseEntity Target {
get {
return target;
}
set {
target = value;
enabled = true;
}
}
private void Awake() {
fireball = GetComponent<FireBall>();
enabled = false;
}
private void FixedUpdate() {
if (!IsValid(target) || target.Health() <= 0) {
fireball.Extinguish();
Destroy(this);
return;
}
fireball.transform.RotateAround(target.transform.position, Vector3.up, 5f);
fireball.transform.hasChanged = true;
}
public void FlingFire(BaseEntity attacker) {
if (fireFlung) return;
fireFlung = true;
mcCoroutine = StartCoroutine(MakeContact(attacker));
}
private IEnumerator MakeContact(BaseEntity attacker) {
float distance = Vector3.Distance(fireball.ServerPosition, attacker.transform.position);
while (!Backbone.Plugin.IsUnloading && attacker != null && fireball != null && !fireball.IsDestroyed && !InRange(fireball.ServerPosition, attacker.transform.position, 2.5f)) {
fireball.ServerPosition = Vector3.MoveTowards(fireball.ServerPosition, attacker.transform.position, distance * 0.1f);
yield return CoroutineEx.waitForSeconds(0.3f);
}
}
private void OnDestroy() {
if (mcCoroutine != null) {
StopCoroutine(mcCoroutine);
mcCoroutine = null;
}
Destroy(this);
}
}
public class PlayerInputEx : FacepunchBehaviour {
public BasePlayer player {
get;
set;
}
private InputState input {
get;
set;
}
private RaidableBase raid {
get;
set;
}
public void Setup(BasePlayer player, RaidableBase raid) {
this.player = player;
this.raid = raid;
raid.Inputs[player] = this;
input = player.serverInput;
InvokeRepeating(Repeater, 0f, 0.1f);
}
public void Restart() {
CancelInvoke(Repeater);
InvokeRepeating(Repeater, 0f, 0.1f);
}
private void Repeater() {
if (raid == null) {
Destroy(this);
return;
}
if (!player || !player.IsConnected) {
raid.TryInvokeResetPayLock();
Destroy(this);
return;
}
TryPlaceLadder(player, raid);
}
public static bool TryPlaceLadder(BasePlayer player, RaidableBase raid) {
if (player.svActiveItemID == 0) {
return false;
}
var input = player.serverInput;
if (!input.WasJustReleased(BUTTON.FIRE_PRIMARY) && !input.IsDown(BUTTON.FIRE_PRIMARY)) {
return false;
}
Item item = player.GetActiveItem();
if (item?.info.shortname != "ladder.wooden.wall") {
return false;
}
RaycastHit hit;
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 4f, Layers.Mask.Construction, QueryTriggerInteraction.Ignore)) {
return false;
}
var block = hit.GetEntity();
if (!block.IsValid() || block.OwnerID != 0 || !Backbone.Path.Blocks.Contains(block.prefabID)) // walls and foundations
{
return false;
}
int amount = item.amount;
var action = new Action(() => {
if (raid == null || item == null || item.amount != amount || IsLadderNear(hit.point)) {
return;
}
var rot = Quaternion.LookRotation(hit.normal, Vector3.up);
var e = GameManager.server.CreateEntity(Backbone.Path.Ladder, hit.point, rot, true);
if (e == null) {
return;
}
e.gameObject.SendMessage("SetDeployedBy", player, SendMessageOptions.DontRequireReceiver);
e.OwnerID = 0;
e.Spawn();
item.UseItem(1);
var planner = item.GetHeldEntity() as Planner;
if (planner != null) {
var deployable = planner?.GetDeployable();
if (deployable != null && deployable.setSocketParent && block.SupportsChildDeployables()) {
e.SetParent(block, true, false);
}
}
raid.Entities.Add(e);
raid.BuiltList.Add(e.net.ID);
}
);
player.Invoke(action, 0.1f);
return true;
}
public static bool IsLadderNear(Vector3 target) {
var list = Pool.GetList<BaseLadder>();
Vis.Entities(target, 0.3f, list, Layers.Mask.Deployed, QueryTriggerInteraction.Ignore);
bool result = list.Count > 0;
Pool.FreeList(ref list);
return result;
}
private void OnDestroy() {
CancelInvoke();
raid?.Inputs?.Remove(player);
Destroy(this);
}
}
public class FinalDestination : FacepunchBehaviour {
public NPCPlayerApex npc;
private List<Vector3> positions;
public AttackOperator.AttackType attackType;
private NpcSettings settings;
private bool isRanged;
private BasePlayer target;
private ulong userID;
public bool stationary;
private bool _isDormant;
private Vector3 home;
private float maxRoamRange;
private void OnDestroy() {
Backbone?.Destinations?.Remove(userID);
CancelInvoke();
Destroy(this);
}
public void Set(bool isDormant) {
if (_isDormant == isDormant || isDormant && npc.AttackTarget is BasePlayer) {
return;
}
_isDormant = npc.IsDormant = isDormant;
if (_isDormant) {
CancelInvoke(Go);
return;
}
npc.IsStopped = false;
npc.RandomMove();
InvokeRepeating(Go, 0f, 7.5f);
}
public void Set(NPCPlayerApex npc, List<Vector3> positions, NpcSettings settings, Vector3 home, float maxRoamRange) {
this.npc = npc;
this.positions = positions;
this.settings = settings;
this.home = home;
this.maxRoamRange = maxRoamRange;
attackType = IsMelee(npc) ? AttackOperator.AttackType.CloseRange : AttackOperator.AttackType.LongRange;
isRanged = attackType != AttackOperator.AttackType.CloseRange;
Backbone.Destinations[userID = npc.userID] = this;
if (!(stationary = positions == null)) {
if (_config.Settings.Management.NpcIsDormant) {
Set(true);
return;
}
InvokeRepeating(Go, 0f, 7.5f);
}
else InvokeRepeating(Attack, 0f, 1f);
}
public void Attack(BasePlayer player, bool converge = true) {
if (_isDormant) {
Set(false);
}
if (target == player) {
return;
}
if (!IsInvoking(Attack)) {
InvokeRepeating(Attack, 0f, 1f);
}
if (npc.Stats.AggressionRange < 150f) {
npc.Stats.AggressionRange += 150f;
npc.Stats.DeaggroRange += 100f;
}
npc.AttackTarget = player;
npc.lastAttacker = player;
npc.AiContext.LastAttacker = player;
target = player;
if (converge) {
Converge(player);
}
}
private void Attack() {
if (stationary) {
StationaryAttack();
return;
}
if (npc.AttackTarget == null || !(npc.AttackTarget is BasePlayer)) {
return;
}
var attacker = npc.AttackTarget as BasePlayer;
if (attacker.IsDead() || !InRange(attacker.transform.position, npc.transform.position, maxRoamRange * 2f)) {
Forget();
CancelInvoke(Attack);
if (_config.Settings.Management.NpcIsDormant) {
Set(true);
return;
}
InvokeRepeating(Go, 0f, 7.5f);
return;
}
if (!npc.GetMounted()) {
npc.NeverMove = false;
npc.IsStopped = false;
npc.RandomMove();
}
if (attacker.IsVisible(npc.eyes.position, attacker.eyes.position)) {
HumanAttackOperator.AttackEnemy(npc.AiContext, attackType);
}
}
private void StationaryAttack() {
npc.SetAimDirection(new Vector3(npc.transform.forward.x, 0, npc.transform.forward.z));
if (npc.AttackTarget == null || !(npc.AttackTarget is BasePlayer)) {
return;
}
var attacker = npc.AttackTarget as BasePlayer;
if (attacker.IsDead()) {
Forget();
return;
}
if (attacker.IsVisible(npc.eyes.position, attacker.eyes.position)) {
npc.SetAimDirection((attacker.transform.position - npc.transform.position).normalized);
HumanAttackOperator.AttackEnemy(npc.AiContext, attackType);
}
}
private void Forget() {
npc.Stats.AggressionRange = settings.AggressionRange;
npc.Stats.DeaggroRange = settings.AggressionRange * 1.125f;
npc.lastDealtDamageTime = Time.time - 21f;
npc.SetFact(Facts.HasEnemy, 0, true, true);
npc.SetFact(Facts.EnemyRange, 3, true, true);
npc.SetFact(Facts.AfraidRange, 1, true, true);
npc.AttackTarget = null;
npc.lastAttacker = null;
npc.lastAttackedTime = Time.time - 31f;
npc.LastAttackedDir = Vector3.zero;
}
public void Warp() {
var position = positions.GetRandom();
npc.Pause();
npc.ServerPosition = position;
npc.GetNavAgent.Warp(position);
npc.stuckDuration = 0f;
npc.IsStuck = false;
npc.Resume();
}
private void Go() {
if (npc.GetMounted()) {
return;
}
if (npc.IsHeadUnderwater()) {
npc.Kill();
Destroy(this);
return;
}
if (npc.AttackTarget == null) {
npc.NeverMove = true;
if (npc.IsStuck) {
Warp();
}
var position = positions.GetRandom();
if (npc.GetNavAgent == null || !npc.GetNavAgent.isOnNavMesh) {
npc.finalDestination = position;
}
else npc.GetNavAgent.SetDestination(position);
npc.IsStopped = false;
npc.Destination = position;
}
}
private void Converge(BasePlayer player) {
foreach (var fd in Backbone.Destinations.Values) {
if (fd != this && fd.npc.IsValid() && fd.npc.AttackTarget == null && fd.npc.IsAlive() && fd.npc.Distance(npc) < 25f) {
if (fd.attackType != attackType) continue;
fd.npc.SetFact(Facts.AllyAttackedRecently, 1, true, true);
fd.npc.SetFact(Facts.AttackedRecently, 1, true, true);
fd.Attack(player, false);
fd.Attack();
}
}
}
private bool IsMelee(BasePlayer player) {
var attackEntity = player.GetHeldEntity() as AttackEntity;
if (attackEntity == null) {
return false;
}
return attackEntity is BaseMelee;
}
public bool NpcCanRoam(Vector3 destination) => InRange(home, destination, maxRoamRange);
}
public class BlockProperties {
public OBB obb;
public Vector3 position;
public uint prefabID;
}
public class BackpackData {
public DroppedItemContainer backpack;
public BasePlayer player;
public ulong userID;
}
public class RaidableBase : FacepunchBehaviour {
private List<BaseMountable> mountables {
get;
set;
}
= Pool.GetList<BaseMountable>();
public Hash<uint, float> conditions {
get;
set;
}
= Pool.Get<Hash<uint, float>>();
private Dictionary<string, string> _clans {
get;
set;
}
= Pool.Get<Dictionary<string, string>>();
private Dictionary<string, List<string>> _friends {
get;
set;
}
= Pool.Get<Dictionary<string, List<string>>>();
public List<StorageContainer> _containers {
get;
set;
}
= Pool.GetList<StorageContainer>();
public List<StorageContainer> _allcontainers {
get;
set;
}
= Pool.GetList<StorageContainer>();
public Dictionary<BasePlayer, PlayerInputEx> Inputs {
get;
set;
}
= Pool.Get<Dictionary<BasePlayer, PlayerInputEx>>();
public List<NPCPlayerApex> npcs {
get;
set;
}
= Pool.GetList<NPCPlayerApex>();
public Dictionary<uint, BasePlayer> records {
get;
set;
}
= Pool.Get<Dictionary<uint, BasePlayer>>();
public Dictionary<ulong, BasePlayer> raiders {
get;
set;
}
= Pool.Get<Dictionary<ulong, BasePlayer>>();
public List<BasePlayer> friends {
get;
set;
}
= Pool.GetList<BasePlayer>();
public List<BasePlayer> intruders {
get;
set;
}
= Pool.GetList<BasePlayer>();
public Dictionary<uint, BackpackData> corpses {
get;
set;
}
= Pool.Get<Dictionary<uint, BackpackData>>();
private Dictionary<FireBall, PlayWithFire> fireballs {
get;
set;
}
= Pool.Get<Dictionary<FireBall, PlayWithFire>>();
private List<Vector3> foundations {
get;
set;
}
= Pool.GetList<Vector3>();
private List<BlockProperties> blockProperties {
get;
set;
}
= Pool.GetList<BlockProperties>();
private List<SphereEntity> spheres {
get;
set;
}
= Pool.GetList<SphereEntity>();
private List<BaseEntity> lights {
get;
set;
}
= Pool.GetList<BaseEntity>();
private List<BaseOven> ovens {
get;
set;
}
= Pool.GetList<BaseOven>();
public List<AutoTurret> turrets {
get;
set;
}
= Pool.GetList<AutoTurret>();
private List<Door> doors {
get;
set;
}
= Pool.GetList<Door>();
private List<CustomDoorManipulator> doorControllers {
get;
set;
}
= Pool.GetList<CustomDoorManipulator>();
public Dictionary<string, float> lastActive {
get;
set;
}
= Pool.Get<Dictionary<string, float>>();
public List<string> ids {
get;
set;
}
= Pool.GetList<string>();
private List<Locker> lockers {
get;
set;
}
= Pool.GetList<Locker>();
private Dictionary<string, ulong> skins {
get;
set;
}
= Pool.Get<Dictionary<string, ulong>>();
public BuildingPrivlidge priv {
get;
set;
}
private Dictionary<string, List<string>> npcKits {
get;
set;
}
private MapMarkerExplosion explosionMarker {
get;
set;
}
private M
// ... [Code preview truncated due to length. Full source in attached file] ...