C#:
using System;
using Facepunch;
using Newtonsoft.Json.Linq;
using Oxide.Core.Plugins;
using Rust;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Oxide.Core;
using UnityEngine;
using UnityEngine.AI;
using Oxide.Plugins.NpcSpawnExtensionMethods;
namespace Oxide.Plugins {
[Info("NpcSpawn", "Sempai#3239", "2.4.4")]
internal class NpcSpawn : RustPlugin {
#region Config
internal class NpcBelt {
public string ShortName;
public int Amount;
public ulong SkinID;
public List<string> Mods;
public string Ammo;
}
internal class NpcWear {
public string ShortName;
public ulong SkinID;
}
internal class NpcConfig {
public string Name {
get;
set;
}
public List<NpcWear> WearItems {
get;
set;
}
public List<NpcBelt> BeltItems {
get;
set;
}
public string Kit {
get;
set;
}
public float Health {
get;
set;
}
public float RoamRange {
get;
set;
}
public float ChaseRange {
get;
set;
}
public float DamageScale {
get;
set;
}
public float TurretDamageScale {
get;
set;
}
public float AimConeScale {
get;
set;
}
public bool DisableRadio {
get;
set;
}
public bool CanUseWeaponMounted {
get;
set;
}
public bool CanRunAwayWater {
get;
set;
}
public float Speed {
get;
set;
}
public int AreaMask {
get;
set;
}
public int AgentTypeID {
get;
set;
}
public string HomePosition {
get;
set;
}
public HashSet<string> States {
get;
set;
}
public SensoryStats Sensory {
get;
set;
}
}
public class SensoryStats {
public float AttackRangeMultiplier {
get;
set;
}
public float SenseRange {
get;
set;
}
public float MemoryDuration {
get;
set;
}
public bool CheckVisionCone {
get;
set;
}
public float VisionCone {
get;
set;
}
}
#endregion Config
#region Methods
private ScientistNPC SpawnNpc(Vector3 position, JObject configJson) {
CustomScientistNpc npc = CreateCustomNpc(position, configJson.ToObject<NpcConfig>());
if (npc != null) {
_scientists.Add(npc.net.ID, npc);
npc.skinID = 11162132011012;
}
return npc;
}
private static CustomScientistNpc CreateCustomNpc(Vector3 position, NpcConfig config) {
ScientistNPC scientistNpc = GameManager.server.CreateEntity("assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_heavy.prefab", position, Quaternion.identity, false) as ScientistNPC;
ScientistBrain scientistBrain = scientistNpc.GetComponent<ScientistBrain>();
CustomScientistNpc customScientist = scientistNpc.gameObject.AddComponent<CustomScientistNpc>();
CustomScientistBrain customScientistBrain = scientistNpc.gameObject.AddComponent<CustomScientistBrain>();
CopySerializableFields(scientistNpc, customScientist);
CopySerializableFields(scientistBrain, customScientistBrain);
UnityEngine.Object.DestroyImmediate(scientistNpc, true);
UnityEngine.Object.DestroyImmediate(scientistBrain, true);
customScientist.Config = config;
customScientist.Brain = customScientistBrain;
customScientist.enableSaving = false;
customScientist.gameObject.AwakeFromInstantiate();
customScientist.Spawn();
return customScientist;
}
private static void CopySerializableFields<T>(T src, T dst) {
FieldInfo[] srcFields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo field in srcFields) {
object value = field.GetValue(src);
field.SetValue(dst, value);
}
}
private void AddTargetRaid(CustomScientistNpc npc, BuildingPrivlidge cupboard) {
if (npc == null || cupboard == null || !_scientists.ContainsKey(npc.net.ID)) return;
npc.AddTargetRaid(cupboard);
}
private void AddTargetGuard(CustomScientistNpc npc, BaseEntity target) {
if (npc == null || target == null || !_scientists.ContainsKey(npc.net.ID)) return;
npc.AddTargetGuard(target);
}
private void ChangeHomePosition(CustomScientistNpc npc, Vector3 pos) {
if (npc == null || !_scientists.ContainsKey(npc.net.ID)) return;
npc.HomePosition = pos;
}
private BasePlayer GetCurrentTarget(CustomScientistNpc npc) {
if (IsCustomScientist(npc)) return npc.CurrentTarget;
else return null;
}
#endregion Methods
#region Controller
internal class DefaultSettings {
public float EffectiveRange;
public float AttackLengthMin;
public float AttackLengthMax;
}
private static readonly Dictionary<string, DefaultSettings> _weapons = new Dictionary<string, DefaultSettings> {
["rifle.bolt"]
= new DefaultSettings {
EffectiveRange = 150f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["bow.compound"] = new DefaultSettings {
EffectiveRange = 15f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["smg.2"] = new DefaultSettings {
EffectiveRange = 20f, AttackLengthMin = 0.4f, AttackLengthMax = 0.4f
}
, ["shotgun.double"] = new DefaultSettings {
EffectiveRange = 10f, AttackLengthMin = 0.3f, AttackLengthMax = 1f
}
, ["pistol.eoka"] = new DefaultSettings {
EffectiveRange = 10f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["rifle.l96"] = new DefaultSettings {
EffectiveRange = 150f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["pistol.nailgun"] = new DefaultSettings {
EffectiveRange = 15f, AttackLengthMin = 0f, AttackLengthMax = 0.46f
}
, ["pistol.python"] = new DefaultSettings {
EffectiveRange = 15f, AttackLengthMin = 0.175f, AttackLengthMax = 0.525f
}
, ["pistol.semiauto"] = new DefaultSettings {
EffectiveRange = 15f, AttackLengthMin = 0f, AttackLengthMax = 0.46f
}
, ["pistol.prototype17"] = new DefaultSettings {
EffectiveRange = 15f, AttackLengthMin = 0f, AttackLengthMax = 0.46f
}
, ["smg.thompson"] = new DefaultSettings {
EffectiveRange = 20f, AttackLengthMin = 0.4f, AttackLengthMax = 0.4f
}
, ["shotgun.waterpipe"] = new DefaultSettings {
EffectiveRange = 10f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["multiplegrenadelauncher"] = new DefaultSettings {
EffectiveRange = 20f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["snowballgun"] = new DefaultSettings {
EffectiveRange = 5f, AttackLengthMin = 2f, AttackLengthMax = 2f
}
}
;
private static readonly HashSet<string> _meleeWeapons = new HashSet<string> {
"bone.club", "knife.bone", "knife.butcher", "candycaneclub", "knife.combat", "longsword", "mace", "machete", "paddle", "pitchfork", "salvaged.cleaver", "salvaged.sword", "spear.stone", "spear.wooden", "chainsaw", "hatchet", "jackhammer", "pickaxe", "axe.salvaged", "hammer.salvaged", "icepick.salvaged", "stonehatchet", "stone.pickaxe", "torch", "sickle", "mace.baseballbat"
}
;
private static readonly HashSet<string> _firstDistanceWeapons = new HashSet<string> {
"bow.compound", "shotgun.double", "pistol.eoka", "flamethrower", "pistol.m92", "pistol.nailgun", "multiplegrenadelauncher", "shotgun.pump", "pistol.python", "pistol.revolver", "pistol.semiauto", "pistol.prototype17", "snowballgun", "shotgun.spas12", "shotgun.waterpipe"
}
;
private static readonly HashSet<string> _secondDistanceWeapons = new HashSet<string> {
"smg.2", "smg.mp5", "rifle.semiauto", "smg.thompson"
}
;
private static readonly HashSet<string> _thirdDistanceWeapons = new HashSet<string> {
"rifle.ak", "rifle.lr300", "lmg.m249", "rifle.m39", "hmlmg", "rifle.ak.ice"
}
;
private static readonly HashSet<string> _fourthDistanceWeapons = new HashSet<string> {
"rifle.bolt", "rifle.l96"
}
;
public class CustomScientistNpc : ScientistNPC {
public NpcConfig Config {
get;
set;
}
public BasePlayer CurrentTarget {
get;
set;
}
public AttackEntity CurrentWeapon {
get;
set;
}
public Vector3 HomePosition {
get;
set;
}
public float DistanceFromBase => Vector3.Distance(transform.position, HomePosition);
public float DistanceToTarget => Vector3.Distance(transform.position, CurrentTarget.transform.position);
public override void ServerInit() {
base.ServerInit();
if (string.IsNullOrEmpty(Config.HomePosition)) HomePosition = transform.position;
else HomePosition = Config.HomePosition.ToVector3();
if (NavAgent == null) NavAgent = GetComponent<NavMeshAgent>();
if (NavAgent != null) {
NavAgent.areaMask = Config.AreaMask;
NavAgent.agentTypeID = Config.AgentTypeID;
}
startHealth = Config.Health;
_health = Config.Health;
damageScale = Config.DamageScale;
if (Config.DisableRadio) {
CancelInvoke(PlayRadioChatter);
RadioChatterEffects = Array.Empty<GameObjectRef>();
DeathEffects = Array.Empty<GameObjectRef>();
}
ClearContainer(inventory.containerWear);
ClearContainer(inventory.containerBelt);
if (!string.IsNullOrEmpty(Config.Kit) && _ins.Kits != null) _ins.Kits.Call("GiveKit", this, Config.Kit);
else UpdateInventory();
displayName = Config.Name;
InvokeRepeating(LightCheck, 1f, 30f);
InvokeRepeating(UpdateTick, 1f, 2f);
}
private void UpdateInventory() {
if (Config.WearItems.Count > 0) {
foreach (Item item in Config.WearItems.Select(x => ItemManager.CreateByName(x.ShortName, 1, x.SkinID))) {
if (item == null) continue;
if (!item.MoveToContainer(inventory.containerWear)) item.Remove();
}
}
if (Config.BeltItems.Count > 0) {
foreach (NpcBelt npcItem in Config.BeltItems) {
Item item = ItemManager.CreateByName(npcItem.ShortName, npcItem.Amount, npcItem.SkinID);
if (item == null) continue;
foreach (ItemDefinition itemDefinition in npcItem.Mods.Select(ItemManager.FindItemDefinition).Where(x => x != null)) item.contents.AddItem(itemDefinition, 1);
if (!item.MoveToContainer(inventory.containerBelt)) item.Remove();
}
}
}
private static void ClearContainer(ItemContainer container) {
List<Item> allItems = container.itemList;
for (int i = allItems.Count - 1; i >= 0; i--) {
Item item = allItems[i];
item.RemoveFromContainer();
item.Remove();
}
}
private void OnDestroy() {
if (_healCoroutine != null) ServerMgr.Instance.StopCoroutine(_healCoroutine);
if (_fireC4Coroutine != null) ServerMgr.Instance.StopCoroutine(_fireC4Coroutine);
if (_fireRocketLauncherCoroutine != null) ServerMgr.Instance.StopCoroutine(_fireRocketLauncherCoroutine);
CancelInvoke();
}
private void UpdateTick() {
if (CanRunAwayWater()) RunAwayWater();
if (CanThrownGrenade()) ThrownGrenade(CurrentTarget.transform.position);
if (CanHeal()) _healCoroutine = ServerMgr.Instance.StartCoroutine(Heal());
EquipWeapon();
if (Config.States.Contains("RaidState") && Foundations.Count == 0) {
if (CurrentTarget == null) {
FirstTarget = null;
CurrentRaidTarget = null;
}
else {
BuildingBlock block = GetNearEntity<BuildingBlock>(CurrentTarget.transform.position, 0.1f, 1 << 21);
if (block.IsExists() && IsTeam(CurrentTarget, block.OwnerID)) FirstTarget = block;
else {
FirstTarget = null;
CurrentRaidTarget = null;
}
}
}
if (_beforeGuardHomePosition != Vector3.zero) {
if (_guardTarget.IsExists()) HomePosition = _guardTarget.transform.position;
else {
HomePosition = _beforeGuardHomePosition;
_beforeGuardHomePosition = Vector3.zero;
_guardTarget = null;
Interface.Oxide.CallHook("OnCustomNpcGuardTargetEnd", this);
}
}
}
#region Targeting
public new BasePlayer GetBestTarget() {
if (IsRunAwayWater) return null;
BasePlayer target = null;
float delta = -1f;
foreach (BasePlayer basePlayer in Brain.Senses.Players) {
if (!CanTargetBasePlayer(basePlayer) || Interface.CallHook("OnCustomNpcTarget", this, basePlayer) != null) continue;
float rangeDelta = 1f - Mathf.InverseLerp(1f, Brain.SenseRange, Vector3.Distance(basePlayer.transform.position, transform.position));
if (Config.Sensory.CheckVisionCone) {
float dot = Vector3.Dot((basePlayer.transform.position - eyes.position).normalized, eyes.BodyForward());
if (dot < Brain.VisionCone) continue;
rangeDelta += Mathf.InverseLerp(Brain.VisionCone, 1f, dot) / 2f;
}
rangeDelta += (Brain.Senses.Memory.IsLOS(basePlayer) ? 2f : 0f);
if (rangeDelta <= delta) continue;
target = basePlayer;
delta = rangeDelta;
}
return target;
}
public bool CanTargetBasePlayer(BasePlayer target) {
if (IsRunAwayWater) return false;
if (!target.IsPlayer() || target.Health() <= 0f) return false;
if (target.IsSleeping() || target.IsWounded() || target.IsDead() || target.InSafeZone()) return false;
if (target._limitedNetworking) return false;
return true;
}
#endregion Targeting
#region Equip Weapons
private bool _isEquiping = false;
private bool CanEquipWeapon() {
if (inventory == null || inventory.containerBelt == null) return false;
if (_isEquiping) return false;
if (IsFireRocketLauncher) return false;
if (_isHealing) return false;
if (IsMounted() && !Config.CanUseWeaponMounted) return false;
return true;
}
public override void EquipWeapon(bool skipDeployDelay = false) {
if (!CanEquipWeapon()) return;
Item weapon = null;
if (CurrentTarget == null) {
if (CurrentWeapon == null) {
Dictionary<int, List<Item>> weapons = new Dictionary<int, List<Item>> {
[0]
= new List<Item>(), [1] = new List<Item>(), [2] = new List<Item>(), [3] = new List<Item>(), [4] = new List<Item>()
}
;
foreach (Item item in inventory.containerBelt.itemList) {
int type = GetTypeWeaponItem(item);
if (type == -1) continue;
weapons[type].Add(item);
}
if (weapons[3].Count > 0) weapon = weapons[3].GetRandom();
else if (weapons[2].Count > 0) weapon = weapons[2].GetRandom();
else if (weapons[1].Count > 0) weapon = weapons[1].GetRandom();
else if (weapons[4].Count > 0) weapon = weapons[4].GetRandom();
else if (weapons[0].Count > 0) weapon = weapons[0].GetRandom();
}
else return;
}
else {
float distanceToTarget = DistanceToTarget;
int type = -1;
foreach (Item item in inventory.containerBelt.itemList) {
int currentType = GetTypeWeaponItem(item);
if (currentType == -1) continue;
if (type == -1) {
weapon = item;
type = currentType;
}
else {
if (type == currentType) continue;
float oldDistance = type > 0 ? Config.Sensory.AttackRangeMultiplier * type * 10f : 2f;
float newDistance = currentType > 0 ? Config.Sensory.AttackRangeMultiplier * currentType * 10f : 2f;
if ((oldDistance > distanceToTarget && newDistance > distanceToTarget && newDistance < oldDistance) || (oldDistance < distanceToTarget && newDistance > distanceToTarget) || (oldDistance < distanceToTarget && newDistance < distanceToTarget && newDistance > oldDistance)) {
weapon = item;
type = currentType;
}
}
}
}
if (weapon == null) return;
AttackEntity attackEntity = weapon.GetHeldEntity() as AttackEntity;
if (attackEntity == null) return;
if (CurrentWeapon == attackEntity) return;
_isEquiping = true;
UpdateActiveItem(weapon.uid);
CurrentWeapon = attackEntity;
attackEntity.TopUpAmmo();
if (attackEntity is Chainsaw) (attackEntity as Chainsaw).ServerNPCStart();
if (attackEntity is BaseProjectile) {
if (_weapons.ContainsKey(weapon.info.shortname)) {
attackEntity.effectiveRange = _weapons[weapon.info.shortname].EffectiveRange;
attackEntity.attackLengthMin = _weapons[weapon.info.shortname].AttackLengthMin;
attackEntity.attackLengthMax = _weapons[weapon.info.shortname].AttackLengthMax;
}
string ammo = Config.BeltItems.FirstOrDefault(x => x.ShortName == weapon.info.shortname).Ammo;
if (!string.IsNullOrEmpty(ammo)) {
BaseProjectile baseProjectile = attackEntity as BaseProjectile;
baseProjectile.primaryMagazine.ammoType = ItemManager.FindItemDefinition(ammo);
baseProjectile.SendNetworkUpdateImmediate();
}
}
Invoke(FinishEquiping, 1.5f);
}
private void FinishEquiping() => _isEquiping = false;
private int GetTypeWeaponItem(Item item) {
if (_meleeWeapons.Contains(item.info.shortname)) return 0;
if (_firstDistanceWeapons.Contains(item.info.shortname)) return 1;
if (_secondDistanceWeapons.Contains(item.info.shortname)) return 2;
if (_thirdDistanceWeapons.Contains(item.info.shortname)) return 3;
if (_fourthDistanceWeapons.Contains(item.info.shortname)) return 4;
return -1;
}
internal void HolsterWeapon() {
if (CurrentWeapon == null) return;
CurrentWeapon.SetHeld(false);
CurrentWeapon = null;
SendNetworkUpdate();
inventory.UpdatedVisibleHolsteredItems();
}
#endregion Equip Weapons
protected override string OverrideCorpseName() => displayName;
public override float GetAimConeScale() => Config.AimConeScale;
#region Heal
private Coroutine _healCoroutine = null;
private bool _isHealing = false;
private bool CanHeal() {
if (_isHealing || health >= Config.Health || CurrentTarget != null || IsFireC4 || IsFireRocketLauncher || _isEquiping) return false;
return inventory.containerBelt.itemList.Any(x => x.info.shortname == "syringe.medical");
}
private IEnumerator Heal() {
_isHealing = true;
Item syringe = inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "syringe.medical");
CurrentWeapon = null;
UpdateActiveItem(syringe.uid);
MedicalTool medicalTool = syringe.GetHeldEntity() as MedicalTool;
yield return CoroutineEx.waitForSeconds(1.5f);
medicalTool.ServerUse();
InitializeHealth(health + 15f > Config.Health ? Config.Health : health + 15f, Config.Health);
yield return CoroutineEx.waitForSeconds(2f);
_isHealing = false;
EquipWeapon();
}
#endregion Heal
#region Grenades
private readonly HashSet<string> _barricades = new HashSet<string> {
"barricade.cover.wood", "barricade.sandbags", "barricade.concrete", "barricade.stone"
}
;
private bool _isReloadGrenade = false;
private bool _isReloadSmoke = false;
private void FinishReloadGrenade() => _isReloadGrenade = false;
private void FinishReloadSmoke() => _isReloadSmoke = false;
private bool CanThrownGrenade() {
if (_isReloadGrenade || CurrentTarget == null) return false;
if (IsMounted() && !Config.CanUseWeaponMounted) return false;
return DistanceToTarget < 15f && inventory.containerBelt.itemList.Any(x => x.info.shortname == "grenade.f1" || x.info.shortname == "grenade.beancan" || x.info.shortname == "grenade.molotov" || x.info.shortname == "grenade.flashbang") && (!CanSeeTarget(CurrentTarget) || IsBehindBarricade());
}
internal bool IsBehindBarricade() => CanSeeTarget(CurrentTarget) && IsBarricade();
private bool IsBarricade() {
SetAimDirection((CurrentTarget.transform.position - transform.position).normalized);
RaycastHit[] hits = Physics.RaycastAll(eyes.HeadRay());
GamePhysics.Sort(hits);
return hits.Select(x => x.GetEntity() as Barricade).Any(x => x != null && _barricades.Contains(x.ShortPrefabName) && Vector3.Distance(transform.position, x.transform.position) < DistanceToTarget);
}
private void ThrownGrenade(Vector3 target) {
Item item = inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "grenade.f1" || x.info.shortname == "grenade.beancan" || x.info.shortname == "grenade.molotov" || x.info.shortname == "grenade.flashbang");
if (item != null) {
GrenadeWeapon weapon = item.GetHeldEntity() as GrenadeWeapon;
if (weapon != null) {
Brain.Navigator.Stop();
SetAimDirection((target - transform.position).normalized);
weapon.ServerThrow(target);
_isReloadGrenade = true;
Invoke(FinishReloadGrenade, 10f);
}
}
}
internal void ThrownSmoke() {
if (!_isReloadSmoke) {
Item item = inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "grenade.smoke");
if (item != null) {
GrenadeWeapon weapon = item.GetHeldEntity() as GrenadeWeapon;
if (weapon != null) {
weapon.ServerThrow(transform.position);
_isReloadSmoke = true;
Invoke(FinishReloadSmoke, 30f);
}
}
}
}
#endregion Grenades
#region Run Away Water
internal bool IsRunAwayWater = false;
private bool CanRunAwayWater() {
if (!Config.CanRunAwayWater || IsRunAwayWater) return false;
if (CurrentTarget == null) {
if (transform.position.y < -0.25f) return true;
else return false;
}
if (transform.position.y > -0.25f || TerrainMeta.HeightMap.GetHeight(CurrentTarget.transform.position) > -0.25f) return false;
if (CurrentWeapon is BaseProjectile && DistanceToTarget < EngagementRange()) return false;
if (CurrentWeapon is BaseMelee && DistanceToTarget < CurrentWeapon.effectiveRange) return false;
return true;
}
private void RunAwayWater() {
IsRunAwayWater = true;
CurrentTarget = null;
Invoke(FinishRunAwayWater, 20f);
}
private void FinishRunAwayWater() => IsRunAwayWater = false;
#endregion Run Away Water
#region Raid
internal bool IsReloadC4 = false;
internal bool IsReloadRocketLauncher = false;
internal bool IsFireRocketLauncher = false;
internal bool IsFireC4 = false;
private Coroutine _fireC4Coroutine = null;
private Coroutine _fireRocketLauncherCoroutine = null;
internal BaseCombatEntity Turret = null;
internal BaseCombatEntity FirstTarget = null;
internal BuildingPrivlidge MainCupboard = null;
internal HashSet<BoxStorage> Boxes = new HashSet<BoxStorage>();
internal HashSet<BuildingPrivlidge> Cupboards = new HashSet<BuildingPrivlidge>();
internal HashSet<BuildingBlock> Foundations = new HashSet<BuildingBlock>();
internal BaseCombatEntity CurrentRaidTarget = null;
internal void AddTargetRaid(BuildingPrivlidge cupboard) {
Cupboards = GetCupboards(cupboard);
Boxes = GetBoxes(Cupboards);
HashSet<BuildingBlock> allBlocks = GetBlocks(Cupboards);
Foundations = allBlocks.Where(x => x.ShortPrefabName.Contains("foundation"));
Vector3 centerHome = GetCenterHomePos(allBlocks);
MainCupboard = Cupboards.Min(x => Vector3.Distance(x.transform.position, centerHome));
Cupboards.Remove(MainCupboard);
}
internal void AddTargetRaidMelee(BuildingPrivlidge cupboard) {
Foundations = GetBlocks(GetCupboards(cupboard)).Where(x => x.ShortPrefabName.Contains("foundation"));
}
internal void AddTurret(BaseCombatEntity turret) {
if (!Turret.IsExists() || Vector3.Distance(transform.position, turret.transform.position) < Vector3.Distance(transform.position, Turret.transform.position)) {
Turret = turret;
BuildingBlock block = GetNearEntity<BuildingBlock>(Turret.transform.position, 0.1f, 1 << 21);
CurrentRaidTarget = block.IsExists() ? block : Turret;
}
}
private static T GetNearEntity<T>(Vector3 position, float radius, int layerMask) where T : BaseCombatEntity {
List<T> list = new List<T>();
Vis.Entities(position, radius, list, layerMask);
return list.Count == 0 ? null : list.Min(s => Vector3.Distance(position, s.transform.position));
}
private static List<T> GetEntities<T>(Vector3 position, float radius, int layerMask) where T : BaseCombatEntity {
List<T> list = new List<T>();
Vis.Entities(position, radius, list, layerMask);
return list.Count == 0 ? null : list;
}
private static Vector3 GetCenterHomePos(HashSet<BuildingBlock> blocks) {
float Xmin = blocks.Min(x => x.transform.position.x).transform.position.x;
float Xmax = blocks.Max(x => x.transform.position.x).transform.position.x;
float Ymin = blocks.Min(x => x.transform.position.y).transform.position.y;
float Ymax = blocks.Max(x => x.transform.position.y).transform.position.y;
float Zmin = blocks.Min(x => x.transform.position.z).transform.position.z;
float Zmax = blocks.Max(x => x.transform.position.z).transform.position.z;
return new Vector3((Xmin + Xmax) / 2, (Ymin + Ymax) / 2, (Zmin + Zmax) / 2);
}
private static HashSet<BuildingPrivlidge> GetCupboards(BuildingPrivlidge cupboard) {
HashSet<ulong> ids = cupboard.authorizedPlayers.Select(x => x.userid).ToHashSet();
return GetEntities<BuildingPrivlidge>(cupboard.transform.position, 100f, 1 << 8).Where(x => x.authorizedPlayers.Any(y => ids.Contains(y.userid))).ToHashSet();
}
private static HashSet<BuildingBlock> GetBlocks(HashSet<BuildingPrivlidge> cupboards) => cupboards.SelectMany(x => x.GetBuilding().buildingBlocks);
private static HashSet<BoxStorage> GetBoxes(HashSet<BuildingPrivlidge> cupboards) => cupboards.SelectMany(x => x.GetBuilding().decayEntities.OfType<BoxStorage>());
internal BaseCombatEntity GetRaidTarget() {
BaseCombatEntity result = GetRaidMainTarget();
if (result == null) return null;
BaseCombatEntity targetPath = GetTargetPath(result);
return targetPath != null ? targetPath : result;
}
internal BaseCombatEntity GetRaidTargetMelee() {
if (Foundations.Count == 0) return null;
foreach (BuildingBlock block in Foundations.Where(x => !x.IsExists())) Foundations.Remove(block);
if (Foundations.Count == 0) return null;
BaseCombatEntity result = Foundations.Min(x => Vector3.Distance(transform.position, x.transform.position));
NavMeshHit navMeshHit;
if (NavMesh.SamplePosition(result.transform.position, out navMeshHit, 3f, NavAgent.areaMask)) {
NavMeshPath path = new NavMeshPath();
if (NavMesh.CalculatePath(transform.position, navMeshHit.position, NavAgent.areaMask, path) && path.status != NavMeshPathStatus.PathComplete) result = GetNearEntity<BaseCombatEntity>(path.corners.Last(), 3f, 1 << 8 | 1 << 21);
}
return result;
}
private BaseCombatEntity GetRaidMainTarget() {
if (Turret.IsExists()) {
BuildingBlock block = GetNearEntity<BuildingBlock>(Turret.transform.position, 0.1f, 1 << 21);
return block.IsExists() ? block : Turret;
}
if (FirstTarget.IsExists()) return FirstTarget;
if (MainCupboard.IsExists()) return MainCupboard;
if (Boxes.Count > 0) {
foreach (BoxStorage storage in Boxes.Where(x => !x.IsExists())) Boxes.Remove(storage);
if (Boxes.Count > 0) return Boxes.Min(x => Vector3.Distance(transform.position, x.transform.position));
}
if (Cupboards.Count > 0) {
foreach (BuildingPrivlidge cupboard in Cupboards.Where(x => !x.IsExists())) Cupboards.Remove(cupboard);
if (Cupboards.Count > 0) return Cupboards.Min(x => Vector3.Distance(transform.position, x.transform.position));
}
if (Foundations.Count > 0) {
foreach (BuildingBlock block in Foundations.Where(x => !x.IsExists())) Foundations.Remove(block);
if (Foundations.Count > 0) return Foundations.Min(x => Vector3.Distance(transform.position, x.transform.position));
}
return null;
}
private BaseCombatEntity GetTargetPath(BaseCombatEntity target) {
NavMeshHit navMeshHit;
int attempts = 0;
while (attempts < 20) {
if (target == null) return null;
attempts++;
float targetHeight = TerrainMeta.HeightMap.GetHeight(target.transform.position);
if (target.transform.position.y - targetHeight > 15f) {
List<BuildingBlock> blocks = GetEntities<BuildingBlock>(new Vector3(target.transform.position.x, targetHeight, target.transform.position.z), 30f, 1 << 21);
if (blocks != null) target = blocks.Min(s => Vector3.Distance(s.transform.position, transform.position));
else return null;
}
if (NavMesh.SamplePosition(target.transform.position, out navMeshHit, 30f, NavAgent.areaMask)) {
NavMeshPath path = new NavMeshPath();
if (NavMesh.CalculatePath(transform.position, navMeshHit.position, NavAgent.areaMask, path)) return path.status == NavMeshPathStatus.PathComplete ? target : GetNearEntity<BaseCombatEntity>(path.corners.Last(), 5f, 1 << 8 | 1 << 21);
}
float x1 = UnityEngine.Random.Range(target.transform.position.x - 30f - 5f, target.transform.position.x - 30f);
float x2 = UnityEngine.Random.Range(target.transform.position.x + 30f, target.transform.position.x + 30f + 5f);
float z1 = UnityEngine.Random.Range(target.transform.position.z - 30f - 5f, target.transform.position.z - 30f);
float z2 = UnityEngine.Random.Range(target.transform.position.z + 30f, target.transform.position.z + 30f + 5f);
Vector3 vector1 = new Vector3(x1, 500f, z1);
vector1.y = TerrainMeta.HeightMap.GetHeight(vector1);
Vector3 vector2 = new Vector3(x2, 500f, z1);
vector2.y = TerrainMeta.HeightMap.GetHeight(vector2);
Vector3 vector3 = new Vector3(x1, 500f, z2);
vector3.y = TerrainMeta.HeightMap.GetHeight(vector3);
Vector3 vector4 = new Vector3(x2, 500f, z2);
vector4.y = TerrainMeta.HeightMap.GetHeight(vector4);
HashSet<Vector3> list = new HashSet<Vector3> {
vector1, vector2, vector3, vector4
}
;
target = GetNearEntity<BaseCombatEntity>(list.Min(x => Vector3.Distance(transform.position, x)), 5f, 1 << 8 | 1 << 21);
}
return null;
}
internal bool StartExplosion(BaseCombatEntity target) {
if (target == null) return false;
if (CanThrownC4(target)) {
_fireC4Coroutine = ServerMgr.Instance.StartCoroutine(ThrownC4(target));
return true;
}
if (CanRaidRocketLauncher(target)) {
ThrownSmoke();
_fireRocketLauncherCoroutine = ServerMgr.Instance.StartCoroutine(ProcessFireRocketLauncher(target));
return true;
}
return false;
}
private bool CanRaidRocketLauncher(BaseCombatEntity target) => !IsReloadRocketLauncher && !IsFireRocketLauncher && inventory.containerBelt.itemList.Any(x => x.info.shortname == "rocket.launcher") && !_isEquiping && !_isHealing && Vector3.Distance(transform.position, target.transform.position) < 30f;
private IEnumerator ProcessFireRocketLauncher(BaseCombatEntity target) {
IsFireRocketLauncher = true;
EquipRocketLauncher();
if (!IsMounted()) SetDucked(true);
Brain.Navigator.Stop();
Brain.Navigator.SetFacingDirectionEntity(target);
yield return CoroutineEx.waitForSeconds(1.5f);
if (target.IsExists()) {
if (target.ShortPrefabName.Contains("foundation")) {
Brain.Navigator.ClearFacingDirectionOverride();
SetAimDirection((target.transform.position - new Vector3(0f, 1.5f, 0f) - transform.position).normalized);
}
FireRocketLauncher();
IsReloadRocketLauncher = true;
Invoke(FinishReloadRocketLauncher, 6f);
}
IsFireRocketLauncher = false;
EquipWeapon();
Brain.Navigator.ClearFacingDirectionOverride();
if (!IsMounted()) SetDucked(false);
}
private void EquipRocketLauncher() {
Item item = inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "rocket.launcher");
CurrentWeapon = null;
UpdateActiveItem(item.uid);
}
private void FireRocketLauncher() {
RaycastHit raycastHit;
SignalBroadcast(Signal.Attack, string.Empty);
Vector3 vector3 = IsMounted() ? eyes.position + new Vector3(0f, 0.5f, 0f) : eyes.position;
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(2.25f, eyes.BodyForward());
float single = 1f;
if (Physics.Raycast(vector3, modifiedAimConeDirection, out raycastHit, single, 1236478737)) single = raycastHit.distance - 0.1f;
TimedExplosive rocket = GameManager.server.CreateEntity("assets/prefabs/ammo/rocket/rocket_basic.prefab", vector3 + modifiedAimConeDirection * single) as TimedExplosive;
rocket.creatorEntity = this;
ServerProjectile serverProjectile = rocket.GetComponent<ServerProjectile>();
serverProjectile.InitializeVelocity(GetInheritedProjectileVelocity(modifiedAimConeDirection) + modifiedAimConeDirection * serverProjectile.speed * 2f);
rocket.Spawn();
}
private void FinishReloadRocketLauncher() => IsReloadRocketLauncher = false;
private bool CanThrownC4(BaseCombatEntity target) => !IsReloadC4 && !IsFireC4 && inventory.containerBelt.itemList.Any(x => x.info.shortname == "explosive.timed") && Vector3.Distance(transform.position, target.transform.position) < 5f;
private IEnumerator ThrownC4(BaseCombatEntity target) {
Item item = inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "explosive.timed");
IsFireC4 = true;
Brain.Navigator.Stop();
Brain.Navigator.SetFacingDirectionEntity(target);
yield return CoroutineEx.waitForSeconds(1.5f);
if (target.IsExists()) {
(item.GetHeldEntity() as ThrownWeapon).ServerThrow(target.transform.position);
IsReloadC4 = true;
Invoke(FinishReloadC4, 15f);
}
IsFireC4 = false;
Brain.Navigator.ClearFacingDirectionOverride();
}
private void FinishReloadC4() => IsReloadC4 = false;
private static bool IsTeam(BasePlayer player, ulong targetId) {
if (player == null || targetId == 0) return false;
if (player.userID == targetId) return true;
if (player.currentTeam != 0) {
RelationshipManager.PlayerTeam playerTeam = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
if (playerTeam == null) return false;
if (playerTeam.members.Contains(targetId)) return true;
}
if (_ins.plugins.Exists("Friends") && (bool)_ins.Friends.Call("AreFriends", player.userID, targetId)) return true;
if (_ins.plugins.Exists("Clans") && _ins.Clans.Author == "k1lly0u" && (bool)_ins.Clans.Call("IsMemberOrAlly", player.UserIDString, targetId.ToString())) return true;
return false;
}
#endregion Raid
#region Guard
private Vector3 _beforeGuardHomePosition = Vector3.zero;
private BaseEntity _guardTarget = null;
internal void AddTargetGuard(BaseEntity target) {
_beforeGuardHomePosition = HomePosition;
_guardTarget = target;
}
#endregion Guard
#region Multiple Grenade Launcher
internal bool IsReloadGrenadeLauncher = false;
private int _countAmmoInGrenadeLauncher = 6;
internal void FireGrenadeLauncher() {
RaycastHit raycastHit;
SignalBroadcast(Signal.Attack, string.Empty);
Vector3 vector3 = IsMounted() ? eyes.position + new Vector3(0f, 0.5f, 0f) : eyes.position;
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(0.675f, eyes.BodyForward());
float single = 1f;
if (Physics.Raycast(vector3, modifiedAimConeDirection, out raycastHit, single, 1236478737)) single = raycastHit.distance - 0.1f;
TimedExplosive grenade = GameManager.server.CreateEntity("assets/prefabs/ammo/40mmgrenade/40mm_grenade_he.prefab", vector3 + modifiedAimConeDirection * single) as TimedExplosive;
grenade.creatorEntity = this;
ServerProjectile serverProjectile = grenade.GetComponent<ServerProjectile>();
serverProjectile.InitializeVelocity(GetInheritedProjectileVelocity(modifiedAimConeDirection) + modifiedAimConeDirection * serverProjectile.speed * 2f);
grenade.Spawn();
_countAmmoInGrenadeLauncher--;
if (_countAmmoInGrenadeLauncher == 0) {
IsReloadGrenadeLauncher = true;
Invoke(FinishReloadGrenadeLauncher, 8f);
}
}
private void FinishReloadGrenadeLauncher() {
_countAmmoInGrenadeLauncher = 6;
IsReloadGrenadeLauncher = false;
}
#endregion Multiple Grenade Launcher
#region Flame Thrower
internal bool IsReloadFlameThrower = false;
internal void FireFlameThrower() {
FlameThrower flameThrower = CurrentWeapon as FlameThrower;
if (flameThrower == null || flameThrower.IsFlameOn()) return;
if (flameThrower.ammo <= 0) {
IsReloadFlameThrower = true;
Invoke(FinishReloadFlameThrower, 4f);
return;
}
flameThrower.SetFlameState(true);
Invoke(flameThrower.StopFlameState, 0.25f);
}
private void FinishReloadFlameThrower() {
FlameThrower flameThrower = CurrentWeapon as FlameThrower;
if (flameThrower == null) return;
flameThrower.TopUpAmmo();
IsReloadFlameThrower = false;
}
#endregion Flame Thrower
#region Melee Weapon
internal void UseMeleeWeapon(bool damage = true) {
BaseMelee weapon = CurrentWeapon as BaseMelee;
if (weapon.HasAttackCooldown()) return;
weapon.StartAttackCooldown(weapon.repeatDelay * 2f);
SignalBroadcast(Signal.Attack, string.Empty, null);
if (weapon.swingEffect.isValid) Effect.server.Run(weapon.swingEffect.resourcePath, weapon.transform.position, Vector3.forward, net.connection, false);
if (!damage) return;
Vector3 vector31 = eyes.BodyForward();
for (int i = 0; i < 2; i++) {
List<RaycastHit> list = Pool.GetList<RaycastHit>();
GamePhysics.TraceAll(new Ray(eyes.position - (vector31 * (i == 0 ? 0f : 0.2f)), vector31), (i == 0 ? 0f : weapon.attackRadius), list, weapon.effectiveRange + 0.2f, 1219701521, QueryTriggerInteraction.UseGlobal, null);
bool flag = false;
for (int j = 0; j < list.Count; j++) {
RaycastHit item = list[j];
BaseEntity entity = item.GetEntity();
if (entity != null && (entity == null || entity != this && !entity.EqualNetID(this)) && (entity == null || !entity.isClient) && entity.Categorize() == "player") {
float single = weapon.damageTypes.Sum(x => x.amount);
entity.OnAttacked(new HitI
// ... [Code preview truncated due to length. Full source in attached file] ...