C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
#define DEBUG
using BaseAIBrain;
using BaseEntity;
using Facepunch;
using JetBrains.Annotations;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Ext.CustomNpc.Gameplay.AI;
using Oxide.Ext.CustomNpc.Gameplay.AI.States;
using Oxide.Ext.CustomNpc.Gameplay.Components;
using Oxide.Ext.CustomNpc.Gameplay.Configurations;
using Oxide.Ext.CustomNpc.Gameplay.Controllers;
using Oxide.Ext.CustomNpc.Gameplay.Controllers.NpcAttire;
using Oxide.Ext.CustomNpc.Gameplay.Data;
using Oxide.Ext.CustomNpc.Gameplay.Entities;
using Oxide.Ext.CustomNpc.Gameplay.Managers;
using Oxide.Ext.CustomNpc.Gameplay.NpcCreator;
using Oxide.Ext.CustomNpc.PluginExntesions;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AI;
//CustomNpcPlugin created with PluginMerge v(1.0.4.0) by MJSU @ https://github.com/dassjosh/Plugin.Merge
namespace Oxide.Plugins {
[Info("CustomNpcPlugin", "Flutes", "0.1.0")]
[Description("test")]
public partial class CustomNpcPlugin : RustPlugin {
#region CustomNpcPlugin.cs
private void Init() {
new PluginsExtensionsManager();
CustomNpc_Manager.Setup();
Unsubscribe("OnInventoryNetworkUpdate");
}
private void Unload() {
CustomNpc_Manager.DestroyAllNpcs();
}
private void SpawnTest(BasePlayer player) {
CustomNpc_Configuration npcConfig = new CustomNpc_Configuration() {
DamageScale = 1, DisableRadio = true, AgentTypeID = -1372625422, MemoryDuration = 10.0f, AreaMask = 1, AttackRangeMultiplier = 1.0f, WearItems = new List<CustomNpc_WearItem> {
new CustomNpc_WearItem {
ShortName = "attire.egg.suit", SkinId = 0
}
}
, BeltItems = new List<CustomNpc_BeltItem> {
new CustomNpc_BeltItem {
ShortName = "rifle.lr300", Amount = 1, SkinId = 0, Mods = new List<string> {
"weapon.mod.holosight", "weapon.mod.flashlight"
}
}
, new CustomNpc_BeltItem {
ShortName = "syringe.medical", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "grenade.f1", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "grenade.smoke", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "explosive.timed", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "rocket.launcher", Amount = 1, SkinId = 0, Mods = new List<string>()
}
}
, CanTargetOtherNpc = false, ChaseRange = 100.0f, CheckVisionCone = false, ListenRange = 10.0f, MaxHealth = 200.0f, StartHealth = 200.0f, RoamRange = 10f, Name = "Test", SenseRange = 50f, Speed = 7.5f, VisionCone = 15.0f, States = new List<string>() {
"DefaultIdleState", "DefaultRoamState", "DefaultChaseState", "DefaultCombatState", "DefaultHealState"
}
}
;
NpcInstantiationFactory.InstanceNpcDefault(player.ServerPosition, npcConfig);
}
#region Oxide Hooks
private void OnEntityKill(CustomNpc_Component customNpc) {
CustomNpc_Manager.OnNpcDestroyed(customNpc);
if (NpcCreator_Manager.IsStarted) {
NpcCreator_Manager.OnEntityKill(customNpc);
}
//NpcCreator_ManagerFactory.OnEntityKill(customNpc);
}
object OnInventoryNetworkUpdate(PlayerInventory inventory, ItemContainer container, ProtoBuf.UpdateItemContainer updateItemContainer, PlayerInventory.Type type, bool broadcast) {
if (inventory.baseEntity == null) return null;
if (NpcCreator_Manager.Controllers.ContainsKey(inventory.baseEntity.userID) == false) return null;
var controller = NpcCreator_Manager.Controllers[inventory.baseEntity.userID];
controller.CopyWearToSelectedNpc();
return null;
}
#endregion
[ChatCommand("customnpc_spawn")]
private void CustomNpcSpawnChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
SpawnTest(player);
}
[ChatCommand("customnpc_creator_start")]
private void CustomNpcCreatorStartChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (args.Length == 0) return;
string pluginName = args[0];
var plugin = Interface.Oxide.RootPluginManager.GetPlugin(pluginName);
if (plugin == null) {
player.ChatMessage($"Plugin {pluginName} not found");
return;
}
if (NpcCreator_Manager.IsStarted == false) Subscribe("OnInventoryNetworkUpdate");
NpcCreator_Manager.StartCreator(player, plugin);
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_stop")]
private void CustomNpcCreatorStopChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
NpcCreator_Manager.StopCreator(player);
if (NpcCreator_Manager.IsStarted == false) Unsubscribe("OnInventoryNetworkUpdate");
}
[ChatCommand("customnpc_creator_stop_all")]
private void CustomNpcCreatorStopAllChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
NpcCreator_Manager.Stop();
Unsubscribe("OnInventoryNetworkUpdate");
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_edit")]
private void CustomNpcCreatorEditChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.InstanceNpcCommand(args);
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_select")]
private void CustomNpcCreatorSelectChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.SelectNpcCommand();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_unselect")]
private void CustomNpcCreatorUnselectChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.UnselectNpcCommand();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_save_all")]
private void CustomNpcCreatorSaveAllChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.SaveAllNpcConfig();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_save_selected")]
private void CustomNpcCreatorSaveSelectedChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.SaveSelectNpcConfig();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_reload_all")]
private void CustomNpcCreatoReloadAllChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.ReloadAllNpcConfig();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_reload_selected")]
private void CustomNpcCreatoReloadSelectedChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.ReloadSelectedNpcConfig();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_test_selected")]
private void CustomNpcCreatorTestSelectedChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.TestSelect();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_test_all")]
private void CustomNpcCreatorTestAllChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.TestAll();
player.ChatMessage($"Success");
}
[ChatCommand("customnpc_creator_test_stop")]
private void CustomNpcCreatorTestEndChatCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin == false) return;
if (NpcCreator_Manager.Controllers.ContainsKey(player.userID) == false) return;
var controller = NpcCreator_Manager.Controllers[player.userID];
controller.StopTest();
player.ChatMessage($"Success");
}
#endregion
#region Configuration.cs
public class Configuration {
private static Configuration m_current;
public static Configuration Current => m_current != null ? m_current : Load();
public static Configuration Load() {
if (ConfigurationFileExist() == false) {
return Create();
}
// else Load
return null;
}
private static bool ConfigurationFileExist() {
return false;
}
private static Configuration Create() {
return null;
}
}
#endregion
#region PluginExntesions\PluginsExtensionsManager.cs
public class PluginsExtensionsManager : IDisposable {
public static PluginsExtensionsManager Instance {
get;
private set;
}
public IReadOnlyDictionary<string, Plugin> LoadedPlugins => m_loadedPlugins;
private Dictionary<string, Plugin> m_loadedPlugins = new Dictionary<string, Plugin>();
private readonly List<string> m_neededPlugins = new List<string>() {
"Kits"
}
;
public PluginsExtensionsManager() {
if (Instance != null) return;
Instance = this;
RegisterPlugins();
RegisterEvents();
}
private void RegisterEvents() {
Interface.Oxide.RootPluginManager.OnPluginAdded += OnPluginAdded;
Interface.Oxide.RootPluginManager.OnPluginAdded -= OnPluginRemoved;
}
private void UnregisterEvents() {
Interface.Oxide.RootPluginManager.OnPluginAdded -= OnPluginAdded;
Interface.Oxide.RootPluginManager.OnPluginAdded -= OnPluginRemoved;
}
private void RegisterPlugins() {
foreach (var pluginName in m_neededPlugins) {
var plugin = Interface.Oxide.RootPluginManager.GetPlugin(pluginName);
if (plugin == null) continue;
m_loadedPlugins.Add(pluginName, plugin);
}
}
private void OnPluginAdded(Plugin plugin) {
if (m_neededPlugins.Contains(plugin.Name) == false) return;
if (m_loadedPlugins.ContainsKey(plugin.Name)) return;
m_loadedPlugins.Add(plugin.Name, plugin);
}
private void OnPluginRemoved(Plugin plugin) {
if (m_neededPlugins.Contains(plugin.Name) == false) return;
if (m_loadedPlugins.ContainsKey(plugin.Name) == false) return;
m_loadedPlugins.Remove(plugin.Name);
}
private bool m_isDisposed;
public void Dispose() {
if (m_isDisposed) {
Interface.Oxide.LogWarning($"attempt to dispose already disposed {nameof(PluginsExtensionsManager)} ");
return;
}
UnregisterEvents();
m_isDisposed = true;
}
}
#endregion
#region Gameplay\AI\CustomNpc_Pathfinding.cs
public class CustomNpc_Pathfinding {
private CustomNpc_Controller m_controller;
private CustomNpc_Component m_component => m_controller.Component;
public CustomNpc_Pathfinding(CustomNpc_Controller controller) {
m_controller = controller;
}
public bool IsEqualsVector3(Vector3 a, Vector3 b) => Vector3.Distance(a, b) < 0.1f;
public void SetDestination(Vector3 pos, float radius, BaseNavigator.NavigationSpeed speed) {
Vector3 sample = GetSamplePosition(pos, radius);
sample.y += 2f;
if (!IsEqualsVector3(sample, m_controller.Brain.Component.Navigator.Destination)) m_controller.Brain.Component.Navigator.SetDestination(sample, speed);
}
public Vector3 GetSamplePosition(Vector3 source, float radius) {
NavMeshHit navMeshHit;
if (NavMesh.SamplePosition(source, out navMeshHit, radius, m_component.NavAgent.areaMask)) {
NavMeshPath path = new NavMeshPath();
if (NavMesh.CalculatePath(m_controller.GameObject.transform.position, navMeshHit.position, m_component.NavAgent.areaMask, path)) {
if (path.status == NavMeshPathStatus.PathComplete) return navMeshHit.position;
else return path.corners.Last();
}
}
return source;
}
public Vector3 GetRandomPositionAround(Vector3 source, float radius) {
Vector2 vector2 = UnityEngine.Random.insideUnitCircle * radius;
return source + new Vector3(vector2.x, 0f, vector2.y);
}
}
#endregion
#region Gameplay\Components\CustomNpcBrain_Component.cs
public class CustomNpcBrain_Component : ScientistBrain {
public Action onAddStates;
public Action onInitializeAI;
public Action<float> onThink;
protected CustomNpcBrain_Controller m_controller;
public virtual void Setup(CustomNpcBrain_Controller controller) {
m_controller = controller;
}
public override void AddStates() {
onAddStates?.Invoke();
}
public override void InitializeAI() {
onInitializeAI?.Invoke();
}
public override void Think(float delta) {
onThink?.Invoke(delta);
}
public void SetThinkRate(float rate) {
thinkRate = rate;
}
public void SetLastThinkTime(float time) {
lastThinkTime = time;
}
}
#endregion
#region Gameplay\Components\CustomNpc_Component.cs
public class CustomNpc_Component : ScientistNPC {
public Action onServerInstantiated;
public Action onDestroy;
public bool IsEquipingWeapon {
get;
private set;
}
public bool IsReloadGrenadeLauncher {
get;
private set;
}
public bool IsReloadFlameThrower {
get;
private set;
}
public bool IsHealing {
get;
private set;
}
public BaseEntity CurrentTarget {
get;
private set;
}
public AttackEntity CurrentWeapon {
get;
private set;
}
public Vector3 HomePosition {
get;
private set;
}
public void Setup(CustomNpcBrain_Component brainComponent, Vector3 homePosition) {
HomePosition = homePosition;
skinID = 11185464824609;
Brain = brainComponent;
enableSaving = false;
gameObject.AwakeFromInstantiate();
}
private void OnDestroy() {
CancelInvoke();
onDestroy?.Invoke();
}
protected override string OverrideCorpseName() => displayName;
public override void ServerInit() {
base.ServerInit();
onServerInstantiated?.Invoke();
}
public void OnEquipingWeapon(AttackEntity weapon) {
CurrentWeapon = weapon;
IsEquipingWeapon = true;
}
public void OnDesequipingWeapon() {
CurrentWeapon = null;
}
public void OnFinishEquipingWeapon() {
IsEquipingWeapon = false;
}
public void SetTarget(BaseEntity target) {
CurrentTarget = target;
}
public void OnHeal() {
IsHealing = true;
}
public void OnFinishHeal() {
IsHealing = false;
}
}
#endregion
#region Gameplay\Configurations\CustomNpc_BeltItem.cs
public class CustomNpc_BeltItem {
public string ShortName;
public ulong SkinId;
public int Amount;
public List<string> Mods = new List<string>();
public string Ammo;
}
#endregion
#region Gameplay\Configurations\CustomNpc_Configuration.cs
public class CustomNpc_Configuration {
public string Name;
public int AreaMask;
public int AgentTypeID;
public float StartHealth;
public float MaxHealth;
public float DamageScale;
public float AttackRangeMultiplier;
public float Speed;
public float MemoryDuration;
public float SenseRange;
public bool CheckVisionCone;
public float ListenRange;
public float VisionCone;
public bool CanRunAwayWater;
public float ChaseRange;
public float RoamRange;
public List<string> States = new List<string>();
public bool DisableRadio;
public string Kit;
public List<CustomNpc_BeltItem> BeltItems = new List<CustomNpc_BeltItem>();
public List<CustomNpc_WearItem> WearItems = new List<CustomNpc_WearItem>();
public bool CanTargetOtherNpc;
public static CustomNpc_Configuration Default() {
CustomNpc_Configuration npcConfig = new CustomNpc_Configuration() {
DamageScale = 1, DisableRadio = true, AgentTypeID = -1372625422, MemoryDuration = 10.0f, AreaMask = 1, AttackRangeMultiplier = 1.0f, WearItems = new List<CustomNpc_WearItem> {
new CustomNpc_WearItem {
ShortName = "attire.egg.suit", SkinId = 0
}
}
, BeltItems = new List<CustomNpc_BeltItem> {
new CustomNpc_BeltItem {
ShortName = "rifle.lr300", Amount = 1, SkinId = 0, Mods = new List<string> {
"weapon.mod.holosight", "weapon.mod.flashlight"
}
}
, new CustomNpc_BeltItem {
ShortName = "syringe.medical", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "grenade.f1", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "grenade.smoke", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "explosive.timed", Amount = 10, SkinId = 0, Mods = new List<string>()
}
, new CustomNpc_BeltItem {
ShortName = "rocket.launcher", Amount = 1, SkinId = 0, Mods = new List<string>()
}
}
, CanTargetOtherNpc = false, ChaseRange = 100.0f, CheckVisionCone = false, ListenRange = 10.0f, MaxHealth = 200.0f, StartHealth = 200.0f, RoamRange = 10f, Name = "Test", SenseRange = 50f, Speed = 7.5f, VisionCone = 15.0f, States = new List<string>() {
"DefaultIdleState", "DefaultRoamState", "DefaultChaseState", "DefaultCombatState", "ChangeWeaponState"
}
}
;
return npcConfig;
}
}
#endregion
#region Gameplay\Configurations\CustomNpc_WearItem.cs
public class CustomNpc_WearItem {
public string ShortName;
public ulong SkinId;
}
#endregion
#region Gameplay\Controllers\CustomNpcBrain_Controller.cs
public class CustomNpcBrain_Controller {
public CustomNpcBrain_Component Component {
get;
private set;
}
private CustomNpc_Controller m_npc;
public CustomNpc_Controller Npc => m_npc;
private bool m_isInit;
public virtual void Initialize(CustomNpc_Controller npc, CustomNpcBrain_Component component) {
Component = component;
m_npc = npc;
component.Setup(this);
Component.onAddStates += AddStates;
Component.onInitializeAI += InitializeAI;
Component.onThink += Think;
m_isInit = true;
}
protected virtual void AddStates() {
if (m_isInit == false) return;
Component.states = new Dictionary<AIState, BasicAIState>();
foreach (var stateName in m_npc.Configuration.States) {
CustomNpc_Manager.IStatesFactory states = null;
if (CustomNpc_Manager.States.TryGetValue(stateName, out states) == false) continue;
var state = states.Get();
state.Setup(m_npc);
Component.AddState(state);
}
if (Component.states.Count == 0) {
Interface.Oxide.LogWarning("Not state added for this npc");
}
}
protected virtual void InitializeAI() {
if (m_isInit == false) {
Interface.Oxide.LogError("[CustomNpc] InitializeAI brain without initialization !!");
return;
}
m_npc.Component.HasBrain = true;
Component.Navigator = m_npc.GameObject.GetComponent<BaseNavigator>();
Component.Navigator.Speed = m_npc.Configuration.Speed;
Component.InvokeRandomized(Component.DoMovementTick, 1f, 0.1f, 0.01f);
Component.AttackRangeMultiplier = m_npc.Configuration.AttackRangeMultiplier;
Component.MemoryDuration = m_npc.Configuration.MemoryDuration;
Component.SenseRange = Math.Abs(m_npc.Configuration.SenseRange);
Component.TargetLostRange = Component.SenseRange * 2f;
Component.VisionCone = Vector3.Dot(Vector3.forward, Quaternion.Euler(0f, m_npc.Configuration.VisionCone, 0f) * Vector3.forward);
Component.CheckVisionCone = m_npc.Configuration.CheckVisionCone;
Component.CheckLOS = true;
Component.IgnoreNonVisionSneakers = true;
Component.MaxGroupSize = 0;
Component.ListenRange = m_npc.Configuration.ListenRange;
Component.HostileTargetsOnly = m_npc.Configuration.SenseRange < 0f;
Component.IgnoreSafeZonePlayers = !Component.HostileTargetsOnly;
Component.SenseTypes = EntityType.Player;
Component.RefreshKnownLOS = false;
Component.IgnoreNonVisionMaxDistance = Component.ListenRange / 3f;
Component.IgnoreSneakersMaxDistance = Component.IgnoreNonVisionMaxDistance / 3f;
Component.Senses.Init(m_npc.Component, Component, Component.MemoryDuration, Component.SenseRange, Component.TargetLostRange, Component.VisionCone, Component.CheckVisionCone, Component.CheckLOS, Component.IgnoreNonVisionSneakers, Component.ListenRange, Component.HostileTargetsOnly, false, Component.IgnoreSafeZonePlayers, Component.SenseTypes, Component.RefreshKnownLOS);
Component.ThinkMode = AIThinkMode.Interval;
Component.SetThinkRate(0.5f);
Component.PathFinder = new HumanPathFinder();
((HumanPathFinder)Component.PathFinder).Init(m_npc.Component);
}
protected virtual void Think(float delta) {
if (m_isInit == false) return;
if (m_npc == null) return;
if (Component == null) return;
if (Component.Senses == null) return;
if (Component.states == null) return;
Component.SetLastThinkTime(Time.time);
if (Component.sleeping) return;
Component.Senses.Update();
m_npc.SetBestTarget();
if (Component.CurrentState != null) {
var thinkResult = Component.CurrentState.StateThink(delta, Component, m_npc.Component);
switch (thinkResult) {
case StateStatus.Finished : case StateStatus.Error : Component.CurrentState.StateLeave(Component, m_npc.Component);
Component.CurrentState = null;
break;
case StateStatus.Running : if (Component.CurrentState.CanLeave() == false) {
return;
}
break;
}
}
float num = 0f;
BasicAIState newState = null;
foreach (BasicAIState value in Component.states.Values) {
if (value == null || value.CanEnter() == false) continue;
float weight = value.GetWeight();
if (weight < num) continue;
num = weight;
newState = value;
}
if (newState != Component.CurrentState) {
Component.CurrentState?.StateLeave(Component, m_npc.Component);
Component.CurrentState = newState;
Component.CurrentState?.StateEnter(Component, m_npc.Component);
}
}
}
#endregion
#region Gameplay\Controllers\CustomNpc_Controller.cs
public class CustomNpc_Controller {
public GameObject GameObject => Component.gameObject;
public CustomNpc_Component Component {
get;
private set;
}
public CustomNpcBrain_Controller Brain {
get;
private set;
}
public CustomNpc_Pathfinding Pathfinding {
get;
private set;
}
public CustomNpc_Configuration Configuration {
get;
private set;
}
public float DistanceToTarget => Vector3.Distance(GameObject.transform.position, Component.CurrentTarget.transform.position);
public float DistanceFromHome => Vector3.Distance(GameObject.transform.position, Component.HomePosition);
public bool IsBehindBarricade() => Component.CanSeeTarget(Component.CurrentTarget) && HasNearbyBarricade();
private Coroutine m_healCoroutine;
private bool m_isInit;
#region Setup
public virtual void Initialize(CustomNpc_Component component, CustomNpc_Configuration configuration) {
Component = component;
Configuration = configuration;
Pathfinding = new CustomNpc_Pathfinding(this);
m_isInit = true;
}
public virtual void Start(CustomNpcBrain_Controller brain) {
if (m_isInit == false) {
Interface.Oxide.LogError("[CustomNpc] Start npc without initialization !!");
return;
}
Brain = brain;
Component.Setup(brain.Component, GameObject.transform.position);
RegisterEvents();
Component.Spawn();
}
private void RegisterEvents() {
Component.onServerInstantiated += OnSpawn;
Component.onDestroy += OnDestroy;
}
protected virtual void OnSpawn() {
if (m_isInit == false) return;
Setup();
}
protected virtual void OnDestroy() {
if (m_isInit == false) return;
if (m_healCoroutine != null) ServerMgr.Instance.StopCoroutine(m_healCoroutine);
if (Brain.Component.CurrentState != null) {
Brain.Component.CurrentState.StateLeave(Brain.Component, Component);
}
Component.onServerInstantiated -= OnSpawn;
Component.onDestroy -= OnDestroy;
}
protected virtual void Setup() {
if (m_isInit == false) return;
Component.displayName = Configuration.Name;
SetupNavAgent();
SetupHealth();
SetupDamages();
SetupSound();
SetupInventory();
Component.InvokeRepeating(Component.LightCheck, 1f, 30f);
// Todo : Config
Component.InvokeRepeating(Update, 1f, 2f);
// Todo : Config
}
protected virtual void SetupNavAgent() {
if (Component.NavAgent == null) Component.NavAgent = GameObject.GetComponent<NavMeshAgent>();
if (Component.NavAgent != null) {
Component.NavAgent.areaMask = Configuration.AreaMask;
Component.NavAgent.agentTypeID = Configuration.AgentTypeID;
}
}
protected virtual void SetupHealth() {
Component.startHealth = Configuration.StartHealth;
Component._maxHealth = Configuration.MaxHealth;
Component._health = Configuration.StartHealth;
}
protected virtual void SetupDamages() {
Component.damageScale = Configuration.DamageScale;
}
protected virtual void SetupSound() {
if (Configuration.DisableRadio) {
Component.CancelInvoke(Component.PlayRadioChatter);
Component.RadioChatterEffects = Array.Empty<GameObjectRef>();
Component.DeathEffects = Array.Empty<GameObjectRef>();
}
}
protected virtual void SetupInventory() {
ClearItems(Component.inventory.containerWear);
ClearItems(Component.inventory.containerBelt);
NpcAttire_Controller_Base attire = null;
if (string.IsNullOrEmpty(Configuration.Kit) == false && PluginsExtensionsManager.Instance.LoadedPlugins.ContainsKey(NpcAttire_Kits_Controller.PLUGIN_NAME)) {
attire = new NpcAttire_Kits_Controller(this, Configuration.Kit);
}
else {
attire = new NpcAttire_Default_Controller(Component.inventory, Configuration.BeltItems, Configuration.WearItems);
}
attire.Equip();
}
#endregion
private void ClearItems(ItemContainer container) {
for (int i = container.itemList.Count - 1; i >= 0; i--) {
Item item = container.itemList[i];
item.RemoveFromContainer();
item.Remove();
}
}
protected virtual void Update() {
if (m_isInit == false) return;
}
protected virtual void SetAimTowardsTarget() {
Vector3 aimDirection = (Component.CurrentTarget.transform.position - GameObject.transform.position).normalized;
Component.SetAimDirection(aimDirection);
}
private IEnumerable<Barricade> GetBarricadeHits(RaycastHit[] hits) {
foreach (var hit in hits) {
var barricade = hit.GetEntity() as Barricade;
if (barricade != null && CoversData.Barricades.Contains(barricade.ShortPrefabName)) {
yield return barricade;
}
}
}
private bool HasNearbyBarricade() {
SetAimTowardsTarget();
RaycastHit[] hits = UnityEngine.Physics.RaycastAll(Component.eyes.HeadRay());
GamePhysics.Sort(hits);
return GetBarricadeHits(hits).Any(barricade => Vector3.Distance(GameObject.transform.position, barricade.transform.position) < DistanceToTarget);
}
#region Weapons
#region Grenade Launcher
public bool IsReloadGrenadeLauncher {
get;
private set;
}
= false;
protected int m_availableAmmoInGrenadeLauncher = 6;
public virtual void FireGrenadeLauncher() {
RaycastHit raycastHit;
Component.SignalBroadcast(Signal.Attack, string.Empty);
Vector3 vector3 = Component.IsMounted() ? Component.eyes.position + new Vector3(0f, 0.5f, 0f) : Component.eyes.position;
Vector3 modifiedAimConeDirection = AimConeUtil.GetModifiedAimConeDirection(0.675f, Component.eyes.BodyForward());
float distance = 1f;
if (UnityEngine.Physics.Raycast(vector3, modifiedAimConeDirection, out raycastHit, distance, 1236478737)) distance = raycastHit.distance - 0.1f;
TimedExplosive grenade = GameManager.server.CreateEntity("assets/prefabs/ammo/40mmgrenade/40mm_grenade_he.prefab", vector3 + modifiedAimConeDirection * distance) as TimedExplosive;
grenade.creatorEntity = Component;
ServerProjectile serverProjectile = grenade.GetComponent<ServerProjectile>();
serverProjectile.InitializeVelocity(Component.GetInheritedProjectileVelocity(modifiedAimConeDirection) + modifiedAimConeDirection * serverProjectile.speed * 2f);
grenade.Spawn();
m_availableAmmoInGrenadeLauncher--;
if (m_availableAmmoInGrenadeLauncher == 0) {
IsReloadGrenadeLauncher = true;
Component.Invoke(FinishReloadGrenadeLauncher, 8f);
}
}
protected void FinishReloadGrenadeLauncher() {
m_availableAmmoInGrenadeLauncher = 6;
IsReloadGrenadeLauncher = false;
}
#endregion Multiple Grenade Launcher
#region Flame Thrower
public bool IsReloadFlameThrower {
get;
private set;
}
public virtual void FireFlameThrower() {
FlameThrower flameThrower = Component.CurrentWeapon as FlameThrower;
if (flameThrower == null || flameThrower.IsFlameOn()) return;
if (flameThrower.ammo <= 0) {
IsReloadFlameThrower = true;
Component.Invoke(FinishReloadFlameThrower, 4f);
return;
}
flameThrower.SetFlameState(true);
Component.Invoke(flameThrower.StopFlameState, 0.25f);
}
protected void FinishReloadFlameThrower() {
FlameThrower flameThrower = Component.CurrentWeapon as FlameThrower;
if (flameThrower == null) return;
flameThrower.TopUpAmmo();
IsReloadFlameThrower = false;
}
#endregion Flame Thrower
#region Melee Weapon
public void UseMeleeWeapon() {
BaseMelee weapon = Component.CurrentWeapon as BaseMelee;
if (weapon.HasAttackCooldown()) return;
weapon.StartAttackCooldown(weapon.repeatDelay * 2f);
Component.SignalBroadcast(Signal.Attack, string.Empty, null);
if (weapon.swingEffect.isValid) Effect.server.Run(weapon.swingEffect.resourcePath, weapon.transform.position, Vector3.forward, Component.net.connection, false);
Vector3 vector31 = Component.eyes.BodyForward();
for (int i = 0; i < 2; i++) {
List<RaycastHit> list = Pool.GetList<RaycastHit>();
GamePhysics.TraceAll(new Ray(Component.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 != Component && !entity.EqualNetID(Component)) && (entity == null || !entity.isClient)) {
float single = weapon.damageTypes.Sum(x => x.amount);
entity.OnAttacked(new HitInfo(Component, entity, DamageType.Slash, single * weapon.npcDamageScale * Configuration.DamageScale));
HitInfo hitInfo = Pool.Get<HitInfo>();
hitInfo.HitEntity = entity;
hitInfo.HitPositionWorld = item.point;
hitInfo.HitNormalWorld = -vector31;
if (entity is BaseNpc || entity is BasePlayer) hitInfo.HitMaterial = StringPool.Get("Flesh");
else hitInfo.HitMaterial = StringPool.Get(item.GetCollider().sharedMaterial != null ? item.GetCollider().sharedMaterial.GetName() : "generic");
weapon.ServerUse_OnHit(hitInfo);
Effect.server.ImpactEffect(hitInfo);
Pool.Free(ref hitInfo);
flag = true;
if (entity == null || entity.ShouldBlockProjectiles()) break;
}
}
Pool.FreeList(ref list);
if (flag) break;
}
}
#endregion Melee Weapon
#endregion
#region Targeting
public virtual void SetBestTarget() {
BaseEntity bestTarget = null;
float highestScore = -1f;
foreach (BaseEntity potentialTarget in Brain.Component.Senses.Players) {
if (!CanTargetEntity(potentialTarget)) continue;
float targetScore = CalculateTargetScore(potentialTarget);
if (targetScore > highestScore) {
bestTarget = potentialTarget;
highestScore = targetScore;
}
}
Component.SetTarget(bestTarget);
}
protected virtual float CalculateTargetScore(BaseEntity entity) {
float distanceScore = 1f - Mathf.InverseLerp(1f, Brain.Component.SenseRange, Vector3.Distance(entity.transform.position, GameObject.transform.position));
float visionScore = Mathf.InverseLerp(Brain.Component.VisionCone, 1f, Vector3.Dot((entity.transform.position - Component.eyes.position).normalized, Component.eyes.BodyForward())) / 2f;
float losBonus = Brain.Component.Senses.Memory.IsLOS(entity) ? 2f : 0f;
return distanceScore + visionScore + losBonus;
}
protected virtual bool CanTargetEntity(BaseEntity target) {
if (target == null || target.IsDestroyed || target.Health() <= 0f) return false;
BasePlayer player = target as BasePlayer;
if (player != null) {
return CanTargetPlayer(player);
}
else {
Drone drone = target as Drone;
if (drone != null) {
return CanTargetDrone(drone);
}
}
return false;
}
private bool CanTargetPlayer(BasePlayer player) {
if (player.IsDead()) return false;
if (player.skinID != 0 && NpcSkinsData.PlayerSkinIDs.Contains(player.skinID)) return true;
if (player.userID.IsSteamId()) return !player.IsSleeping() && !player.IsWounded() && !player._limitedNetworking;
NPCPlayer npcPlayer = player as NPCPlayer;
if (npcPlayer != null) return CanTargetNpcPlayer(npcPlayer);
return false;
}
protected virtual bool CanTargetNpcPlayer(NPCPlayer npcPlayer) {
return npcPlayer is FrankensteinPet || Configuration.CanTargetOtherNpc;
}
protected virtual bool CanTargetDrone(Drone drone) {
return !(Component.CurrentWeapon is BaseMelee);
}
#endregion
public void Heal() {
m_healCoroutine = ServerMgr.Instance.StartCoroutine(HealCoroutine());
}
private IEnumerator HealCoroutine() {
Component.OnHeal();
Item syringe = Component.inventory.containerBelt.itemList.FirstOrDefault(x => x.info.shortname == "syringe.medical");
Component.OnDesequipingWeapon();
Component.UpdateActiveItem(syringe.uid);
MedicalTool medicalTool = syringe.GetHeldEntity() as MedicalTool;
yield return CoroutineEx.waitForSeconds(1.5f);
if (medicalTool != null) medicalTool.ServerUse();
Component.InitializeHealth(Component.health + 15f > Configuration.MaxHealth ? Configuration.MaxHealth : Component.health + 15f, Configuration.MaxHealth);
yield return CoroutineEx.waitForSeconds(3f);
Component.OnFinishHeal();
}
}
#endregion
#region Gameplay\Data\CoversData.cs
public static class CoversData {
public static readonly HashSet<string> Barricades = new HashSet<string> {
"barricade.cover.wood", "barricade.sandbags", "barricade.concrete", "barricade.stone"
}
;
}
#endregion
#region Gameplay\Data\NpcSkinsData.cs
public static class NpcSkinsData {
public static HashSet<ulong> PlayerSkinIDs = new HashSet<ulong> {
14922524, 19395142091920
}
;
}
#endregion
#region Gameplay\Data\WeaponsData.cs
public static class WeaponsData {
public class Range {
public float EffectiveRange;
public float AttackLengthMin;
public float AttackLengthMax;
}
public static readonly Dictionary<string, Range> WeaponsRange = new Dictionary<string, Range> {
["snowballgun"]
= new Range {
EffectiveRange = 5f, AttackLengthMin = 2f, AttackLengthMax = 2f
}
, ["shotgun.double"] = new Range {
EffectiveRange = 10f, AttackLengthMin = 0.3f, AttackLengthMax = 1f
}
, ["pistol.eoka"] = new Range {
EffectiveRange = 10f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["shotgun.waterpipe"] = new Range {
EffectiveRange = 10f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["speargun"] = new Range {
EffectiveRange = 15f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["bow.compound"] = new Range {
EffectiveRange = 15f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["crossbow"] = new Range {
EffectiveRange = 15f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
, ["bow.hunting"] = new Range {
EffectiveRange = 15f, AttackLengthMin = -1f, AttackLengthMax = -1f
}
,
// ... [Code preview truncated due to length. Full source in attached file] ...