C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Linq;
using System.Collections;
using System.Globalization;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
namespace Oxide.Plugins
{
[Info("NPC Grenades", "Sempai#3239", "1.2.2")]
[Description("F1 grenades spawn various NPCs when thrown.")]
public class NPCGrenades: CovalencePlugin
{
#region plugin refs
[PluginReference]
private Plugin Friends, Clans, Kits;
#endregion
#region Consts
private static NPCGrenades plugin;
private static System.Random random = new System.Random();
private static VersionNumber previousVersion;
public const int f1GrenadeId = 143803535;
public const ulong scientistSkinID = 2640541557;
public const ulong heavySkinID = 2640541496;
public const ulong juggernautSkinID = 2647297156;
public const ulong tunnelSkinID = 2676146196;
public const ulong underwaterSkinID = 2676146329;
public const ulong murdererSkinID = 2643502595;
public const ulong scarecrowSkinID = 2647297210;
public const ulong mummySkinID = 2643385137;
public const ulong bearSkinID = 2647301111;
public const ulong polarbearSkinID = 2868239755;
public const ulong wolfSkinID = 2647303718;
public const ulong boarSkinID = 2643502513;
public const ulong stagSkinID = 2647297256;
public const ulong chickenSkinID = 2647297056;
public const ulong bradleySkinID = 2643385052;
public const string scientistPrefab = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab";
public const string heavyPrefab = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_heavy.prefab";
public const string scarecrowPrefab = "assets/prefabs/npc/scarecrow/scarecrow.prefab";
public const string bearPrefab = "assets/rust.ai/agents/bear/bear.prefab";
public const string polarbearPrefab = "assets/rust.ai/agents/bear/polarbear.prefab";
public const string wolfPrefab = "assets/rust.ai/agents/wolf/wolf.prefab";
public const string boarPrefab = "assets/rust.ai/agents/boar/boar.prefab";
public const string stagPrefab = "assets/rust.ai/agents/stag/stag.prefab";
public const string chickenPrefab = "assets/rust.ai/agents/chicken/chicken.prefab";
public const string bradleyPrefab = "assets/prefabs/npc/m2bradley/bradleyapc.prefab";
public const string murdererChatter = "assets/prefabs/npc/murderer/sound/breathing.prefab";
public const string murdererDeath = "assets/prefabs/npc/murderer/sound/death.prefab";
public const string fleshBloodImpact = "assets/bundled/prefabs/fx/impacts/slash/flesh/fleshbloodimpact.prefab";
public const string explosionSound = "assets/prefabs/weapons/f1 grenade/effects/f1grenade_explosion.prefab";
public const string bradleyExplosion = "assets/prefabs/npc/m2bradley/effects/bradley_explosion.prefab";
public const string permScientist = "npcgrenades.scientist";
public const string permHeavy = "npcgrenades.heavy";
public const string permJuggernaut = "npcgrenades.juggernaut";
public const string permTunnel = "npcgrenades.tunnel";
public const string permUnderwater = "npcgrenades.underwater";
public const string permMurderer = "npcgrenades.murderer";
public const string permScarecrow = "npcgrenades.scarecrow";
public const string permMummy = "npcgrenades.mummy";
public const string permBear = "npcgrenades.bear";
public const string permPolarbear = "npcgrenades.polarbear";
public const string permWolf = "npcgrenades.wolf";
public const string permBoar = "npcgrenades.boar";
public const string permStag = "npcgrenades.stag";
public const string permChicken = "npcgrenades.chicken";
public const string permBradley = "npcgrenades.bradley";
public const string permAdmin = "npcgrenades.admin";
#endregion
#region Language
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Syntax"] = "Invalid syntax, use: /npcnade.give <type> <SteamID/PlayerName>",
["WrongType"] = "Grenade type \"{0}\" not recognised, please check & try again.",
["Received"] = "You received a {0}!",
["PlayerReceived"] = "Player {0} ({1}) received a {2}!",
["Permission"] = "You do not have permission to use {0}!",
["NotEnabled"] = "{0} is not enabled!",
["NotAdmin"] = "You do not have permission to use that command!",
["PlayerNotFound"] = "Can't find a player with the name or ID: {0}",
["PlayersFound"] = "Multiple players found, please be more specific: {0}",
["UnderWater"] = "{0} ({1}) spawned under water and was killed.",
["InSafeZone"] = "{0} ({1}) spawned in as Safe Zone and was killed.",
["Outside"] = "{0} ({1}) spawned inside and was killed.",
["OnStructure"] = "{0} ({1}) spawned on a building and was killed.",
["IsInRock"] = "{0} ({1}) spawned inside terrain and was killed.",
["ConsoleSyntax"] = "Invalid syntax, use: npcnade.give <type> <SteamID/PlayerName>",
["InvalidNade"] = "Grenade type \"{0}\" not recognised, please check and try again!"
}, this);
}
private string GetMessage(string messageKey, string playerID, params object[] args) {
return string.Format(lang.GetMessage(messageKey, this, playerID), args);
}
private void Message(IPlayer player, string messageKey, params object[] args)
{
var message = GetMessage(messageKey, player.Id, args);
if (config.options.usePrefix)
{
player.Reply(config.options.chatPrefix + message);
}
else
{
player.Reply(message);
}
}
private void Message(BasePlayer player, string messageKey, params object[] args)
{
if (player is NPCPlayer)
{
return;
}
var message = GetMessage(messageKey, player.UserIDString, args);
if (config.options.usePrefix && config.options.chatPrefix != string.Empty)
{
player.ChatMessage(config.options.chatPrefix + message);
}
else
{
player.ChatMessage(message);
}
}
#endregion
#region Oxide Hooks
private void OnServerInitialized()
{
plugin = this;
}
private void Init()
{
LoadNadeInfo();
permission.RegisterPermission(permAdmin, this);
foreach (var item in NadeInfo.Keys)
{
permission.RegisterPermission(NadeInfo[item].Perm, this);
}
try
{
storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
if (storedData == null)
{
Puts("Data file is blank.Creating default data file.");
LoadDefaultData();
}
}
catch (Exception ex)
{
if (ex is JsonSerializationException || ex is NullReferenceException || ex is JsonReaderException)
{
Puts($"Exception Type: {ex.GetType()}");
Puts("Data file contains errors.Either fix the errors or delete the data file and reload the plugin for default values.");
return;
}
throw;
}
LoadDefaultData();
}
private void Unload()
{
plugin = null;
NadeInfo = null;
GrenadeNPCData = null;
BaseNpcData = null;
BradleyAPCData = null;
NPCInventories = null;
}
private void OnServerSave()
{
int delay = random.Next(10, 30);
timer.Once(delay, () =>
{
SaveData();
});
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
{
if (IsNpcGrenade(entity.skinID))
{
NPCPlayer npc = entity as NPCPlayer;
if (npc is ScientistNPC || npc is ScarecrowNPC)
{
if (npc?.userID != null && GrenadeNPCData.ContainsKey(npc.userID) && !NPCInventories.ContainsKey(npc.userID))
{
npc.children.Clear();
ItemContainer[] source = { npc.inventory.containerMain, npc.inventory.containerWear, npc.inventory.containerBelt };
Inv npcInv = new Inv() { name = npc.displayName, };
NPCInventories.Add(npc.userID, npcInv);
for (int i = 0; i < source.Length; i++)
{
foreach (var item in source[i].itemList)
{
npcInv.inventory[i].Add(new InvContents
{
ID = item.info.itemid,
amount = item.amount,
skinID = item.skin,
});
}
}
}
timer.Once(5.0f, () =>
{
RemoveNpcData(npc.userID);
});
}
else
{
RemoveNpcData(entity.net.ID);
}
}
}
// Below adds proper grenade names to items added via kits/loadouts by plugins which don't specify a name
// in the config.This means when players click on the item in inventory it keeps the NPC Nade name.// It is also needed for checks later on.private void OnItemAddedToContainer(ItemContainer container, Item item)
{
if (IsNpcGrenade(item.skin))
{
if (item.name == null)
{
if (NadeInfo.ContainsKey(item.skin))
{
item.name = NadeInfo[item.skin].Name;
}
}
}
}
// This hook works regardless of whether player or NPC player.OnExplosiveThrown only hooks for players.private void OnExplosiveFuseSet(TimedExplosive explosive, float fuseLength)
{
var npc = explosive.creatorEntity as BasePlayer;
if (npc == null || !npc.IsAlive())
return;
var item = npc.GetActiveItem();
if (item == null || explosive == null)
return;
if (IsNpcGrenade(item.skin))
{
OnNpcNadeThrown(npc, explosive, item);
}
}
// Changed to a custom call due to OnExplosiveThrown not hooking NPC throws
private void OnNpcNadeThrown(BaseEntity thrower, BaseEntity entity, Item npcNade)
{
var player = thrower as BasePlayer;
if (!IsNpcGrenade(npcNade.skin))
{
return;
}
else if (!NadeInfo[npcNade.skin].Enabled)
{
NextTick(() => {
entity.Kill();
});
if (player is NPCPlayer)
{
Puts($"An NPC player is trying to throw an NPCGrenade which is not enabled in the config: {npcNade.name}");
return;
}
else
{
GiveNade(player, npcNade.skin, npcNade.name, "refund");
Message(player, "NotEnabled", npcNade.name);
return;
}
}
if (config.options.usePerms && !HasPermission(player, npcNade))
{
NextTick(() => {
entity.Kill();
});
GiveNade(player, npcNade.skin, npcNade.name, "refund");
Message(player, "Permission", npcNade.name);
return;
}
timer.Once(2.4f, () =>
{
if (entity != null)
{
var position = entity.transform.position;
NextTick(() => {
entity.Kill();
});
if (player == null || !player.IsAlive())
{
return;
}
if (storedData.HumanNPC.ContainsKey(npcNade.name))
{
NPCPlayerData settings = new NPCPlayerData
{
Name = storedData.HumanNPC[npcNade.name].Name,
Prefab = storedData.HumanNPC[npcNade.name].Prefab,
Health = storedData.HumanNPC[npcNade.name].Health,
MaxRoamRange = storedData.HumanNPC[npcNade.name].MaxRoamRange,
SenseRange = storedData.HumanNPC[npcNade.name].SenseRange,
ListenRange = storedData.HumanNPC[npcNade.name].ListenRange,
AggroRange = storedData.HumanNPC[npcNade.name].AggroRange,
DeAggroRange = storedData.HumanNPC[npcNade.name].DeAggroRange,
TargetLostRange = storedData.HumanNPC[npcNade.name].TargetLostRange,
MemoryDuration = storedData.HumanNPC[npcNade.name].MemoryDuration,
VisionCone = storedData.HumanNPC[npcNade.name].VisionCone,
CheckVisionCone = storedData.HumanNPC[npcNade.name].CheckVisionCone,
CheckLOS = storedData.HumanNPC[npcNade.name].CheckLOS,
IgnoreNonVisionSneakers = storedData.HumanNPC[npcNade.name].IgnoreNonVisionSneakers,
DamageScale = storedData.HumanNPC[npcNade.name].DamageScale,
PeaceKeeper = storedData.HumanNPC[npcNade.name].PeaceKeeper,
IgnoreSafeZonePlayers = storedData.HumanNPC[npcNade.name].IgnoreSafeZonePlayers,
RadioChatter = storedData.HumanNPC[npcNade.name].RadioChatter,
DeathSound = storedData.HumanNPC[npcNade.name].DeathSound,
NumberToSpawn = storedData.HumanNPC[npcNade.name].NumberToSpawn,
SpawnRadius = storedData.HumanNPC[npcNade.name].SpawnRadius,
DespawnTime = storedData.HumanNPC[npcNade.name].DespawnTime,
KillInSafeZone = storedData.HumanNPC[npcNade.name].KillInSafeZone,
StripCorpseLoot = storedData.HumanNPC[npcNade.name].StripCorpseLoot,
KitList = storedData.HumanNPC[npcNade.name].KitList,
Speed = storedData.HumanNPC[npcNade.name].Speed,
Acceleration = storedData.HumanNPC[npcNade.name].Acceleration,
FastSpeedFraction = storedData.HumanNPC[npcNade.name].FastSpeedFraction,
NormalSpeedFraction = storedData.HumanNPC[npcNade.name].NormalSpeedFraction,
SlowSpeedFraction = storedData.HumanNPC[npcNade.name].SlowSpeedFraction,
SlowestSpeedFraction = storedData.HumanNPC[npcNade.name].SlowestSpeedFraction,
LowHealthMaxSpeedFraction = storedData.HumanNPC[npcNade.name].LowHealthMaxSpeedFraction,
TurnSpeed = storedData.HumanNPC[npcNade.name].TurnSpeed,
ExplosionSound = storedData.HumanNPC[npcNade.name].ExplosionSound
};
if (settings.Prefab.Contains("scarecrow"))
{
SpawnScarecrow(player, npcNade, position, settings);
return;
}
else
{
SpawnScientist(player, npcNade, position, settings);
return;
}
}
else if (storedData.AnimalNPC.ContainsKey(npcNade.name))
{
SpawnAnimal(player, npcNade, position);
return;
}
else if (storedData.BradleyNPC.ContainsKey(npcNade.name))
{
SpawnBradley(player, npcNade, position);
return;
}
}
});
}
private void OnExplosiveDropped(BasePlayer player, BaseEntity entity, Item item)
{
if (item.skin == null)
{
return;
}
else if (!IsNpcGrenade(item.skin))
{
return;
}
OnNpcNadeThrown(player, entity, item);
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null)
{
return null;
}
if (IsNpcGrenade(entity.skinID))
{
// Stop melee armed NPC injuring themselves which happens when their swing misses the target
if (info.Initiator == info.HitEntity)
{
return true;
}
}
if (!config.bradley.bradleyBaseDamage)
{
var damageType = info.damageTypes.GetMajorityDamageType();
if (entity is BasePlayer)
{
return null;
}
else if (damageType == DamageType.Blunt)
{
if(entity.GetBuildingPrivilege() && info.WeaponPrefab.name == "MainCannonShell")
{
// Blunt damage from APC cannon shells blocked
CancelHit(info);
return true;
}
}
else if (damageType == DamageType.Bullet && info.Initiator is BradleyAPC)
{
// Bullet damage from APC machine gun bullets blocked
CancelHit(info);
return true;
}
}
return null;
}
private object OnNpcTarget(BaseEntity entity, BaseEntity target)
{
if (entity != null && target != null)
{
var isEntityNade = IsNpcGrenade(entity.skinID);
var isTargetNade = IsNpcGrenade(target.skinID);
if (isEntityNade && isTargetNade)
{
return true;
}
else if (isEntityNade && !isTargetNade)
{
// Target is Player
if (target is BasePlayer)
{
var player = target as BasePlayer;
if (player.IsSleeping() && config.options.sleeperSafe)
{
return true;
}
if (!config.options.attackOwner && entity.OwnerID == player.userID)
{
return true;
}
if (config.options.useFriends || config.options.useClans || config.options.useTeams)
{
if (entity.OwnerID != player.userID && IsFriend(entity.OwnerID, player.userID))
{
return true;
}
return null;
}
}
else if (target is NPCPlayer)
{
if (config.options.npcSafe)
{
return true;
}
}
else if (target is BaseNpc)
{
if (config.options.npcSafe)
{
return true;
}
}
}
else if (!isEntityNade && isTargetNade)
{
if (entity is NPCPlayer && config.options.npcSafe)
{
return true;
}
else if (entity is BaseNpc && config.options.animalSafe)
{
return true;
}
}
}
return null;
}
private object OnTurretTarget(AutoTurret turret, BaseEntity target)
{
if (target != null)
{
if (config.options.turretSafe && IsNpcGrenade(target.skinID))
{
return true;
}
}
return null;
}
private object CanBradleyApcTarget(BradleyAPC bradley, BaseEntity target)
{
if (target != null)
{
if (IsNpcGrenade(target.skinID))
{
if (config.options.bradleySafe)
{
return false;
}
}
var player = target as BasePlayer;
if (IsNpcGrenade(bradley.skinID))
{
if (player.IsSleeping() && config.options.sleeperSafe)
{
return false;
}
if (!config.options.attackOwner && bradley.OwnerID == player.userID)
{
return false;
}
if (config.options.useFriends || config.options.useClans || config.options.useTeams)
{
if (bradley.OwnerID != player.userID && IsFriend(bradley.OwnerID, player.userID))
{
return true;
}
}
}
}
return null;
}
private object OnBradleyApcInitialize(BradleyAPC bradley)
{
if (!IsNpcGrenade(bradley.skinID))
{
return null;
}
else
{
var key = "Bradley Grenade";
bradley.health = storedData.BradleyNPC[key].Health;
bradley._maxHealth = storedData.BradleyNPC[key].Health;
bradley.searchRange = storedData.BradleyNPC[key].SearchRange;
bradley.viewDistance = storedData.BradleyNPC[key].ViewDistance;
bradley.maxCratesToSpawn = storedData.BradleyNPC[key].CratesToSpawn;
bradley.throttle = storedData.BradleyNPC[key].ThrottleResponse;
bradley.leftThrottle = bradley.throttle;
bradley.rightThrottle = bradley.throttle;
bradley.ClearPath();
bradley.currentPath.Clear();
bradley.currentPathIndex = 0;
bradley.DoAI = true;
bradley.DoSimpleAI();
var position = bradley.transform.position;
for (int i = 0; i < storedData.BradleyNPC[key].PatrolPathNodes; i++)
{
position = position + UnityEngine.Random.onUnitSphere * storedData.BradleyNPC[key].PatrolRange;
position.y = TerrainMeta.HeightMap.GetHeight(position);
bradley.currentPath.Add(position);
}
return true;
}
return null;
}
void OnEntitySpawned(NPCPlayerCorpse corpse)
{
if (corpse == null)
{
return;
}
Inv npcInv = new Inv();
timer.Once(0.2f, () =>
{
if (corpse == null || corpse.IsDestroyed)
{
return;
}
ulong id = corpse.playerSteamID;
if (!NPCInventories.ContainsKey(id))
{
return;
}
npcInv = NPCInventories[id];
corpse._playerName = npcInv.name;
corpse.lootPanelName = npcInv.name;
var key = corpse._playerName + " Grenade";
if (storedData.HumanNPC[key].StripCorpseLoot)
{
corpse.containers[0].Clear();
}
else
{
for (int i = 0; i < npcInv.inventory.Length; i++)
{
foreach (var item in npcInv.inventory[i])
{
var giveItem = ItemManager.CreateByItemID(item.ID, item.amount, item.skinID);
if (!giveItem.MoveToContainer(corpse.containers[i], -1, true))
giveItem.Remove();
}
}
}
timer.Once(5f, () => NPCInventories?.Remove(id));
});
}
#endregion
#region Main
private void SpawnScientist(BasePlayer player, Item npcNade, Vector3 position, NPCPlayerData settings)
{
if (player == null || npcNade == null || position == null || settings == null)
{
return;
}
DoExplosion(settings.ExplosionSound, position);
for (int i = 0; i < settings.NumberToSpawn; i++)
{
if (settings.NumberToSpawn > 1)
{
position = position + UnityEngine.Random.onUnitSphere * settings.SpawnRadius;
position.y = TerrainMeta.HeightMap.GetHeight(position);
}
var npc = (ScientistNPC)GameManager.server.CreateEntity(settings.Prefab, position + new Vector3(0, 0.1f, 0), new Quaternion(), true);
if (npc == null)
{
return;
}
npc.Spawn();
var nav = npc.GetComponent<BaseNavigator>();
if (nav == null)
{
return;
}
npc.NavAgent.enabled = true;
nav.CanUseNavMesh = true;
nav.DefaultArea = "Walkable";
npc.NavAgent.areaMask = 1;
npc.NavAgent.agentTypeID = -1372625422;
npc.NavAgent.autoTraverseOffMeshLink = true;
npc.NavAgent.autoRepath = true;
nav.CanUseCustomNav = true;
npc.NavAgent.baseOffset = -0.1f;
nav.PlaceOnNavMesh();
var brain = npc.gameObject.AddComponent<ScientistAI>();
brain.Settings = settings;
var move = npc.gameObject.AddComponent<ScientistMovement>();
move.HomeLoc = position;
move.Settings = settings;
npc.enableSaving = false;
npc.skinID = npcNade.skin;
npc.OwnerID = player.userID;
npc.displayName = settings.Name;
npc.damageScale = settings.DamageScale;
npc.startHealth = settings.Health;
npc.InitializeHealth(settings.Health, settings.Health);
npc.EnablePlayerCollider();
timer.Once(0.2f, () =>
{
if (npc == null || npc.IsDestroyed)
{
return;
}
if (SpawnAborted(player, npc, npcNade, position, npc.userID))
{
return;
}
GiveGrenadeNpcKit(npc, npcNade);
if (settings.DespawnTime > 0)
{
DespawnNPC(npc, npc.userID, settings.DespawnTime);
}
GrenadeNPCData.Add(npc.userID, npc);
});
}
}
private void SpawnScarecrow(BasePlayer player, Item npcNade, Vector3 position, NPCPlayerData settings)
{
if (player == null || npcNade == null || position == null || settings == null)
{
return;
}
DoExplosion(settings.ExplosionSound, position);
for (int i = 0; i < settings.NumberToSpawn; i++)
{
if (settings.NumberToSpawn > 1)
{
position = position + UnityEngine.Random.onUnitSphere * settings.SpawnRadius;
position.y = TerrainMeta.HeightMap.GetHeight(position);
}
var npc = (ScarecrowNPC)GameManager.server.CreateEntity(settings.Prefab, position + new Vector3(0, 0.1f, 0), new Quaternion(), true);
if (npc == null)
{
return;
}
npc.Spawn();
var nav = npc.GetComponent<BaseNavigator>();
if (nav == null)
{
return;
}
npc.NavAgent.enabled = true;
nav.CanUseNavMesh = true;
nav.DefaultArea = "Walkable";
npc.NavAgent.areaMask = 1;
npc.NavAgent.agentTypeID = -1372625422;
npc.NavAgent.autoTraverseOffMeshLink = true;
npc.NavAgent.autoRepath = true;
nav.CanUseCustomNav = true;
npc.NavAgent.baseOffset = -0.1f;
nav.PlaceOnNavMesh();
var brain = npc.gameObject.AddComponent<ScarecrowAI>();
brain.Settings = settings;
var move = npc.gameObject.AddComponent<ScarecrowMovement>();
move.HomeLoc = position;
move.Settings = settings;
npc.enableSaving = false;
npc.skinID = npcNade.skin;
npc.OwnerID = player.userID;
npc.displayName = settings.Name;
npc.damageScale = settings.DamageScale;
npc.startHealth = settings.Health;
npc.InitializeHealth(settings.Health, settings.Health);
npc.EnablePlayerCollider();
timer.Once(0.2f, () =>
{
if (npc == null || npc.IsDestroyed)
{
return;
}
if (SpawnAborted(player, npc, npcNade, position, npc.userID))
{
return;
}
GiveGrenadeNpcKit(npc, npcNade);
if (settings.DespawnTime > 0)
{
DespawnNPC(npc, npc.userID, settings.DespawnTime);
}
GrenadeNPCData.Add(npc.userID, npc);
});
}
}
private void SpawnAnimal(BasePlayer player, Item npcNade, Vector3 position)
{
if (storedData.AnimalNPC.ContainsKey(npcNade.name))
{
string npcPrefab = storedData.AnimalNPC[npcNade.name].Prefab;
string npcName = storedData.AnimalNPC[npcNade.name].Name;
int spawnAmount = storedData.AnimalNPC[npcNade.name].NumberToSpawn;
string exploSound = storedData.AnimalNPC[npcNade.name].ExplosionSound;
DoExplosion(exploSound, position);
for (int i = 0; i < spawnAmount; i++)
{
if (spawnAmount > 1)
{
position = position + UnityEngine.Random.onUnitSphere * storedData.AnimalNPC[npcNade.name].SpawnRadius;
position.y = TerrainMeta.HeightMap.GetHeight(position);
}
BaseNpc npc = (BaseNpc)GameManager.server.CreateEntity(npcPrefab, position);
npc.Spawn();
npc.startHealth = storedData.AnimalNPC[npcNade.name].Health;
npc.InitializeHealth(storedData.AnimalNPC[npcNade.name].Health, storedData.AnimalNPC[npcNade.name].Health);
npc.OwnerID = player.userID;
npc.skinID = npcNade.skin;
npc.CurrentBehaviour = BaseNpc.Behaviour.Attack;
npc.SetFact(BaseNpc.Facts.CanTargetEnemies, 1, true, true);
npc.SetFact(BaseNpc.Facts.IsAggro, 1, true, true);
if (SpawnAborted(player, npc, npcNade, position, npc.net.ID))
{
return;
}
float despawnTime = storedData.AnimalNPC[npcNade.name].DespawnTime;
if (despawnTime > 0)
{
DespawnNPC(npc, npc.net.ID, despawnTime);
}
BaseNpcData.Add(npc.net.ID, npc);
}
}
}
private void SpawnBradley(BasePlayer player, Item npcNade, Vector3 position)
{
if (storedData.BradleyNPC.ContainsKey(npcNade.name))
{
string npcPrefab = storedData.BradleyNPC[npcNade.name].Prefab;
string npcName = storedData.BradleyNPC[npcNade.name].Name;
int spawnAmount = storedData.BradleyNPC[npcNade.name].NumberToSpawn;
string exploSound = storedData.BradleyNPC[npcNade.name].ExplosionSound;
DoExplosion(exploSound, position);
for (int i = 0; i < spawnAmount; i++)
{
if (spawnAmount > 1)
{
position = position + UnityEngine.Random.onUnitSphere * storedData.BradleyNPC[npcNade.name].SpawnRadius;
position.y = TerrainMeta.HeightMap.GetHeight(position);
}
BradleyAPC npc = (BradleyAPC)GameManager.server.CreateEntity(npcPrefab, position);
npc.OwnerID = player.userID;
npc.skinID = npcNade.skin;
npc.Spawn();
if (SpawnAborted(player, npc, npcNade, position, npc.net.ID))
{
return;
}
float despawnTime = storedData.BradleyNPC[npcNade.name].DespawnTime;
if (despawnTime > 0)
{
DespawnNPC(npc, npc.net.ID, despawnTime);
}
BradleyAPCData.Add(npc.net.ID, npc);
}
}
}
private void DespawnNPC(BaseEntity npc, ulong botId, float despawnTime)
{
timer.Once(despawnTime, () =>
{
if (npc != null && !npc.IsDestroyed)
{
NextTick(() =>
{
npc.Kill();
RemoveNpcData(botId);
});
return;
}
});
}
#endregion
#region Helpers
private void CancelHit(HitInfo info)
{
info.damageTypes = new DamageTypeList();
info.DidHit = false;
info.DoHitEffects = false;
}
private object OnNpcKits(ulong npcUserID) // Prevents conflict with NPCKits.{
return GrenadeNPCData.ContainsKey(npcUserID) ? true : (object)null;
}
private void GiveNade(BasePlayer player, ulong skinId, string nadeName, string reason)
{
if (player == null && skinId == null || nadeName == null || reason == null)
{
return;
}
Item npcNade = ItemManager.CreateByItemID(f1GrenadeId, 1, skinId);
npcNade.name = nadeName;
if (reason == "give")
{
player.inventory.GiveItem(npcNade);
Message(player, "Received", nadeName);
}
else if (reason == "refund")
{
player.inventory.GiveItem(npcNade);
}
}
public Item GiveInventoryItem(ItemContainer itemContainer, string shortName, int itemAmount, ulong skinId)
{
Item item = ItemManager.CreateByName(shortName, itemAmount, skinId);
if (item == null) return null;
if (!item.MoveToContainer(itemContainer))
{
item.Remove(0f);
return null;
}
return item;
}
private bool HasPermission(BasePlayer player, Item npcNade)
{
if (player == null || npcNade == null)
{
return false;
}
if (player is NPCPlayer)
{
// Allow NPCs spawned by plugins which arm with NPCGrenades to work regardless of perms
return true;
}
else if (storedData.HumanNPC.ContainsKey(npcNade.name))
{
string humanPerm = storedData.HumanNPC[npcNade.name].Permission;
if (permission.UserHasPermission(player.UserIDString, humanPerm))
{
return true;
}
}
else if (storedData.AnimalNPC.ContainsKey(npcNade.name))
{
string animalPerm = storedData.AnimalNPC[npcNade.name].Permission;
if (permission.UserHasPermission(player.UserIDString, animalPerm))
{
return true;
}
}
else if (storedData.BradleyNPC.ContainsKey(npcNade.name))
{
string apcPerm = storedData.BradleyNPC[npcNade.name].Permission;
if (permission.UserHasPermission(player.UserIDString, apcPerm))
{
return true;
}
}
return false;
}
private bool IsNpcGrenade(ulong skinId)
{
if(NadeInfo.ContainsKey(skinId))
{
return true;
}
return false;
}
public Vector3 GetNavPoint(Vector3 position)
{
NavMeshHit hit;
if (!NavMesh.SamplePosition(position, out hit, 5, -1))
{
return position;
}
else if (Physics.RaycastAll(hit.position + new Vector3(0, 100, 0), Vector3.down, 99f, 1235288065).Any())
{
return position;
}
else if (hit.position.y < TerrainMeta.WaterMap.GetHeight(hit.position))
{
return position;
}
position = hit.position;
return position;
}
private bool IsInSafeZone(Vector3 position)
{
int loop = Physics.OverlapSphereNonAlloc(position, 1f, Vis.colBuffer, 1 << 18, QueryTriggerInteraction.Collide);
for (int i = 0; i < loop; i++)
{
Collider collider = Vis.colBuffer[i];
if (collider.GetComponent<TriggerSafeZone>())
{
return true;
}
}
return false;
}
private bool IsOnStructure(Vector3 position)
{
RaycastHit hit;
var heightOffset = new Vector3(0, 0.1f, 0);
if (Physics.Raycast(position + heightOffset, Vector3.down,
out hit, 4f, LayerMask.GetMask("Construction")) && hit.GetEntity().IsValid())
{
if (hit.GetEntity().name.Contains("building"))
{
return true;
}
}
return false;
}
private bool IsInRock(Vector3 position)
{
RaycastHit hit;
string[] colliders = new string[] { "rock", "cliff", "junk", "range", "invisible" };
Physics.queriesHitBackfaces = true;
if (Physics.Raycast(position, Vector3.up, out hit, 25f, 65536, QueryTriggerInteraction.Ignore))
{
if (colliders.Any(x => hit.collider?.gameObject?.name.Contains(x, CompareOptions.OrdinalIgnoreCase) != null))
{
return true;
}
}
Physics.queriesHitBackfaces = false;
return false;
}
private void DoExplosion(string sound, Vector3 position)
{
try
{
Effect.server.Run(sound, position);
}
catch
{
Puts($"Invalid explosion effect path specified.");
}
}
private IPlayer FindPlayer(string nameOrIdOrIp)
{
foreach (var activePlayer in covalence.Players.Connected)
{
if (activePlayer.Id == nameOrIdOrIp)
return activePlayer;
if (activePlayer.Name.Contains(nameOrIdOrIp))
return activePlayer;
if (activePlayer.Name.ToLower().Contains(nameOrIdOrIp.ToLower()))
return activePlayer;
if (activePlayer.Address == nameOrIdOrIp)
return activePlayer;
}
return null;
}
private bool IsFriend(ulong playerId, ulong targetId)
{
if (playerId == 0 || targetId == 0)
{
return false;
}
if (playerId == targetId)
{
return true;
}
if (Clans)
{
var result = Clans?.Call("IsMemberOrAlly", playerId, targetId);
if (result != null && Convert.ToBoolean(result))
{
return true;
}
}
if (Friends)
{
var result = Friends?.Call("AreFriends", playerId, targetId);
if (result != null && Convert.ToBoolean(result))
{
return true;
}
}
RelationshipManager.PlayerTeam team;
RelationshipManager.ServerInstance.playerToTeam.TryGetValue(playerId, out team);
if (team == null)
{
return false;
}
if (team.members.Contains(targetId))
{
return true;
}
return false;
}
private bool SpawnAborted(BasePlayer player, BaseEntity npc, Item npcNade, Vector3 position, ulong npcId)
{
if (npc == null || npc.IsDestroyed)
{
return true;
}
if (npc.WaterFactor() > 0.7f)
{
npc.Kill();
Message(player, "UnderWater", npcNade.name, npcId);
return true;
}
else if (IsInSafeZone(position))
{
npc.Kill();
Message(player, "InSafeZone", npcNade.name, npcId);
return true;
}
else if (!npc.IsOutside())
{
npc.Kill();
Message(player, "Outside", npcNade.name, npcId);
return true;
}
else if (IsOnStructure(position))
{
npc.Kill();
Message(player, "OnStructure", npcNade.name, npcId);
return true;
}
else if (IsInRock(position))
{
npc.Kill();
Message(player, "IsInRock", npcNade.name, npcId);
return true;
}
return false;
}
private void RemoveNpcData(ulong npcId)
{
if (GrenadeNPCData.ContainsKey(npcId))
{
GrenadeNPCData.Remove(npcId);
}
else if (BaseNpcData.ContainsKey(npcId))
{
BaseNpcData.Remove(npcId);
}
else if (BradleyAPCData.ContainsKey(npcId))
{
BradleyAPCData.Remove(npcId);
}
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
}
#endregion
#region Kits
private void GiveGrenadeNpcKit(NPCPlayer npc, Item npcNade)
{
if (storedData.HumanNPC.ContainsKey(npcNade.name))
{
int kitCount = storedData.HumanNPC[npcNade.name].KitList.Count();
if (Kits)
{
if (kitCount > 0)
{
var kit = storedData.HumanNPC[npcNade.name].KitList[random.Next(kitCount)];
object kitCheck = Kits?.CallHook("GetKitInfo", kit, true);
if (kitCheck == null)
{
Puts($"Kit: \"{kit}\" does not exist, using default kit.");
}
else
{
npc.inventory.Strip();
Kits?.Call($"GiveKit", npc, kit, true);
return;
}
}
}
if (npc == null) return;
npc.inventory.Strip();
foreach (var item in storedData.HumanNPC[npcNade.name].DefaultLoadout)
{
var container = item.Container.ToLower();
if (container == "belt")
{
GiveInventoryItem(npc.inventory.containerBelt, item.Shortname, item.Amount, item.SkinID);
}
else if (container == "wear")
{
GiveInventoryItem(npc.inventory.containerWear, item.Shortname, item.Amount, item.SkinID);
}
else
{
GiveInventoryItem(npc.inventory.containerMain, item.Shortname, item.Amount, item.SkinID);
}
}
}
}
#endregion
#region Commands
[Command("npcnade.give")]
private void CmdGiveNpcNade(IPlayer player, string command, string[] args)
{
if (!player.HasPermission(permAdmin))
{
Message(player, "NotAdmin");
return;
}
else if (args.Length < 2)
{
// ... [Code preview truncated due to length. Full source in attached file] ...