C#:
using Rust;
using Rust.Ai;
using Rust.Registry;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Network;
using Network.Visibility;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.AI;
using Facepunch;
namespace Oxide.Plugins {
[Info("Bradley Guards", "Krungh Crow", "1.6.7", ResourceId = 183)]
[Description("Bradley spawns A tank Crew when destroyed")]
class BradleyGuards : RustPlugin {
#region Changelogs and ToDo
/*******************************************************************************************
*
* v1.1.2 : fixed Guard npc not carrying a weapon if kit is not existing
* v1.1.3 : BradleyGuards and npc will not target each other
* v1.1.4 : Patched for BradleyTiers
* v1.1.5 : Added that Guards will ignore sleepers
* : Improved Mono with extra waterchecks and removes the Guards if they happen to walk into water (submerged)
* : Improved the spawning if in buildings and will find a new spawn location
* : Some coding cleanup (regions)
* : Guards will not recieve fireball damage from the bradley fires
* v1.2.0 : Added ResourceID
* : Performance Update
* v1.2.1 : Fixed Bradley Guards despawn on plugin un/reload
* : Fixed npc from targeting the Bradley Guards through global settings
* v1.2.2 : Patched for december 2nd rust update.
* v1.2.3 : Fixed oxide mixing up HumanNPC plugin(same name) and npc type
* v1.3.0 : Fix for NPC attackrange
* v1.3.1 : Patched various AI behaviour
* : NPC now ignore players in safezone
* : Fixed NPC being Idle after spawning
* : Changed NPC spawnheight
* : Switched from junkpile to roam scientists
* : Skips HP and Crate settings if using Monument Bradley plugin
* v1.3.2 : Added Navhit check on npc spawn
* : Npc are forced on navmesh
* : Added npc spawnradius (10 by default) in config
* v1.3.3 : Patched for conflict with BotReSpawn (by steenamaroo)
* v1.3.4 : if Bradley spawns with less then 100hp destoys it
* v1.4.0 : Added support for Convoy
* : Added MLRS rockets on lastditcheffort
* Side effect samsites can target the rockets
* : Removed Bradley health check and kill
* : Added API : bool IsBradleyNpc(global::HumanNPC npc)
*
* object OnEntityDeath(global::HumanNPC npc, HitInfo info)
{
if (npc == null) return null;
if (BradleyGuards.Call<bool>("IsBradleyNpc", npc))
{
Puts($"Target : {npc} net.ID : [{npc.net.ID}] Event : BradleyGuards NPC");
}
return null;
}
* v1.4.1 : Possible fix for the new nre
* : Added better check if Convoy is installed
* : Added (Bradley Crew Body to Bags) to destroy corpse and drops the npc loot
* : Updated msg system (no need to redo language files)
* : Fixed endless mlrs rockets when amount is set to 0
* : Fixed npc will despawn instead of die when life duration ends
* : Fixed the Radio sound when npc are killed
* v1.4.2 : Possible fix for runaway npc
* : Added support for BetterNpcNames
* v1.4.3 : Added support for ArmoredTrain
* : Pre patched for ArmoredTrain plugin update
* v1.4.4 : Hotfix for using same triggers
* : Fixed Max crate spawns
* : Fixed Health
* : Fixed exploit npc corpses having their gears when using BetterNpcNames(v1.0.3)
* v1.4.5 : Possible fix for Targeting when BG and ArmoredTrain is installed
* : BG ignores max amount of bradleycrates to spawn when the bradley is ArmoredTrain APC
* v1.4.6 : Static samsites will ignore the MLRS rockets
* : Added MLRS rocket radius
* : Added Last Ditch Effort Hardmode
* : Repositioned console notification BG event started
* v1.4.7 : Removed ownerid checks to avoid locked bradleys from other plugins
* v1.4.8 : Fix for compile issues
* v1.4.9 : Patched for 6/7/2023 Rust Update
* v1.5.0 : Possible Roaming fix
* v1.5.1 : Bradley ignores sleepers or players in safezone
* : Removed admin perm
* : Fixed permissions
* : Possible fix for npc strip issue on Carbon Servers
* v1.5.2 : Possible fix for runaway npc
* : When going vanished npc should return roaming unless attacked first
* : Fixed Bradley targeting players in Vanish when BradleyOptions is not loaded
* : Replaced Fire rockets for the actual Molotovs
* : Removed some redundant code
* : Possible fix for NRE strip
* : Molotovs will not spamm trigger the nades and mollotovs thrown by the bradley
* v1.5.3 : Patch for bradleytargeting nre
* v1.6.0 : External plugin cfg changed to use triggers incl npc spawning (true/false)
* : Optimised checks for Convoy,ArmoredTrain and Satdish/HarborEvent
* : Reworked the NPC spawn calls to be able to be used as Api for BradleyEvents/BradleyTiers
* : Fixed APC target override for event/custom APC's
* : Added Option to replace molotovs with firerockets
* : Added support for BradleyEvents and BradleyDrops plugins
* v1.6.1 : Optimised OnSamSiteTarget trigger
* Updated API IsBradleyNpc now returns null or true
* Added API : OnBradleyNPCDeath(global::HumanNPC npc, HitInfo info)
* Added API : OnBradleyNPCGotHit(global::HumanNPC npc, HitInfo info)
* v1.6.2 : Changed npc type to (scientistnpc_bradleyapc_monument)
* v1.6.3 : Reverted back to scientistnpc_roam since new one was removed
* v1.6.5 : Patched for dec rust update
* v1.6.6 : Updates for fires with TruePVE compatibility (patched by Nivex)
*
*******************************************************************************************/
#endregion
[PluginReference]
Plugin ArmoredTrain , BetterNpcNames, Bradley, BradleyTiers, BradleyEvents, Convoy, Kits;
#region OnServerInitialized
void OnServerInitialized() => instance = this;
#endregion
#region Variables
private readonly List<ulong> _Crew = new List<ulong>();
const ulong chaticon = 76561198410558650;
const string sci = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_roam.prefab";
//const string sci = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_bradley.prefab";
//const string sci = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_bradley_heavy.prefab";
const string bomb1 = "assets/prefabs/weapons/beancan grenade/grenade.beancan.deployed.prefab";
const string bomb2 = "assets/prefabs/weapons/f1 grenade/grenade.f1.deployed.prefab";
const string molotov = "assets/prefabs/weapons/molotov cocktail/grenade.molotov.deployed.prefab";
const string rocketfire = "assets/prefabs/ammo/rocket/rocket_fire.prefab";
const string MLRS = "assets/content/vehicles/mlrs/rocket_mlrs.prefab";
const string ShowMessages_Perm = "bradleyguards.ShowMessages";
const string ShowKillMessages_Perm = "bradleyguards.ShowKillMessages";
string prefix;
bool NPCTarget;
#endregion
#region Mono
public static BradleyGuards instance;
public static Dictionary<BasePlayer, BradleyCrew> SpawnedCrew {
get;
set;
}
= new Dictionary<BasePlayer, BradleyCrew>();
void Unload() {
RemoveAllNpc();
}
public class BradleyCrew : FacepunchBehaviour {
public global : : HumanNPC npc;
public bool ReturningToHome = false;
public bool isRoaming = true;
public Vector3 SpawnPoint;
void Start() {
npc = GetComponent<global : : HumanNPC>();
InvokeRepeating("GoHome", 1.0f, 5.0f);
Invoke(nameof(_UseBrain), 0.1f);
}
public void _UseBrain() {
#region navigation
npc.Brain.Navigator.Agent.agentTypeID = -1372625422;
npc.Brain.Navigator.DefaultArea = "Walkable";
npc.Brain.Navigator.enabled = true;
npc.Brain.Navigator.CanUseNavMesh = true;
npc.Brain.Navigator.BestCoverPointMaxDistance = instance.configData.Event.CrewRoam /2;
npc.Brain.Navigator.BestRoamPointMaxDistance = instance.configData.Event.CrewRoam;
npc.Brain.Navigator.MaxRoamDistanceFromHome = instance.configData.Event.CrewRoam;
npc.Brain.Navigator.Init(npc, npc.Brain.Navigator.Agent);
npc.Brain.Navigator.SetDestination(SpawnPoint, BaseNavigator.NavigationSpeed.Slow, 0f, 0f);
#endregion
#region senses & Targeting
npc.Brain.ForceSetAge(0);
npc.Brain.AllowedToSleep = false;
npc.Brain.sleeping = false;
npc.Brain.SenseRange = 30f;
npc.Brain.ListenRange = 40f;
npc.Brain.TargetLostRange = 25f;
npc.Brain.HostileTargetsOnly = false;
npc.Brain.IgnoreSafeZonePlayers = true;
#endregion
}
void GoHome() {
if (npc == null || npc.IsDestroyed || npc.isMounted) return;
if (!npc.HasBrain) return;
if (npc.Brain.Senses.Memory.Targets.Count > 0) {
for (var i = 0; i < npc.Brain.Senses.Memory.Targets.Count; i++) {
BaseEntity target = npc.Brain.Senses.Memory.Targets[i];
BasePlayer player = target as BasePlayer;
if (target == null || !player.IsAlive()) {
WipeMemory();
ReturningToHome = true;
isRoaming = false;
return;
}
if (npc.Distance(player.transform.position) > 40f) {
WipeMemory();
ReturningToHome = true;
isRoaming = false;
return;
}
}
}
var distanceHome = Vector3.Distance(npc.transform.position, SpawnPoint);
if (ReturningToHome == false) {
if (isRoaming == true && distanceHome > instance.configData.Event.CrewRoam) {
ReturningToHome = true;
isRoaming = false;
return;
}
if (isRoaming == true && distanceHome < instance.configData.Event.CrewRoam) {
Vector3 random = UnityEngine.Random.insideUnitCircle.normalized * 10f;
Vector3 newPos = instance.GetNavPoint(SpawnPoint + new Vector3(random.x, 0f, random.y));
SettargetDestination(newPos);
return;
}
}
if (ReturningToHome && distanceHome > 2) {
if (npc.Brain.Navigator.Destination == SpawnPoint) return;
WipeMemory();
SettargetDestination(SpawnPoint);
return;
}
ReturningToHome = false;
isRoaming = true;
}
private void SettargetDestination(Vector3 position) {
npc.Brain.Navigator.Destination = position;
npc.Brain.Navigator.SetDestination(position, BaseNavigator.NavigationSpeed.Slow, 0f, 0f);
}
void WipeMemory() {
if (!npc.HasBrain) return;
npc.Brain.Senses.Players.Clear();
npc.Brain.Senses.Memory.Players.Clear();
npc.Brain.Senses.Memory.Targets.Clear();
npc.Brain.Senses.Memory.Threats.Clear();
npc.Brain.Senses.Memory.LOS.Clear();
npc.Brain.Senses.Memory.All.Clear();
}
void OnDestroy() {
if (npc != null && !npc.IsDestroyed) {
npc.Kill();
}
CancelInvoke("GoHome");
CancelInvoke(nameof(_UseBrain));
}
}
#endregion
#region Configuration
void Init() {
if (!LoadConfigVariables()) {
Puts("Config file issue detected. Please delete file, or check syntax and fix.");
return;
}
permission.RegisterPermission(ShowMessages_Perm, this);
permission.RegisterPermission(ShowKillMessages_Perm, this);
NPCTarget = configData.NPCTarget;
prefix = lang.GetMessage("Prefix", this);
}
private ConfigData configData;
class ConfigData {
[JsonProperty(PropertyName = "Global NPC dont target")]
public bool NPCTarget = false;
[JsonProperty(PropertyName = "External Plugins")]
public SettingsExternal External = new SettingsExternal();
[JsonProperty(PropertyName = "Bradley Settings")]
public SettingsBradley Bradley = new SettingsBradley();
[JsonProperty(PropertyName = "Bradley Loot")]
public SettingsBradleyLoot BradleyLoot = new SettingsBradleyLoot();
[JsonProperty(PropertyName = "Bradley Canon")]
public SettingsBradleyCanon BradleyCanon = new SettingsBradleyCanon();
[JsonProperty(PropertyName = "Bradley Machinegun")]
public SettingsBradleyMachinegun BradleyMachineGun = new SettingsBradleyMachinegun();
[JsonProperty(PropertyName = "Event Settings")]
public SettingsEvent Event = new SettingsEvent();
[JsonProperty(PropertyName = "Tank Crew Driver")]
public TankCrewDriver TankCrewDriver = new TankCrewDriver();
[JsonProperty(PropertyName = "Tank Crew Guards")]
public TankCrewGuards TankCrewGuards = new TankCrewGuards();
[JsonProperty(PropertyName = "Tank Crew Gunners")]
public TankCrewGunner TankCrewGunner = new TankCrewGunner();
}
class SettingsExternal {
[JsonProperty(PropertyName = "Armored Train : Can trigger Events")]
public bool CanTriggerTrain = false;
[JsonProperty(PropertyName = "Convoy : Can trigger Events")]
public bool CanTriggerConv = false;
[JsonProperty(PropertyName = "SatDish/Harbor Event : Can trigger Events")]
public bool CanTriggerSatdish = false;
[JsonProperty(PropertyName = "BradleyDrops : Can trigger Events")]
public bool CanTriggerBradleyDrops = false;
[JsonProperty(PropertyName = "BetterNpcNames : Driver Title")]
public string DriverTitle = "Driver";
[JsonProperty(PropertyName = "BetterNpcNames : Guard Title")]
public string GuardTitle = "Commander";
[JsonProperty(PropertyName = "BetterNpcNames : Gunner Title")]
public string GunnerTitle = "Gunner";
}
class SettingsBradley {
[JsonProperty(PropertyName = "Change Values")]
public bool BSettingsUse = true;
[JsonProperty(PropertyName = "Bradley Health")]
public int BradleyHealth = 2000;
[JsonProperty(PropertyName = "Bradley Max Fire Range")]
public int BradleyMaxFireRange = 100;
[JsonProperty(PropertyName = "Bradley Throttle Responce")]
public float BradleySpeed = 1.0f;
}
class SettingsBradleyCanon {
[JsonProperty(PropertyName = "Change Values")]
public bool BCSettingsUse = true;
[JsonProperty(PropertyName = "Bradley Canon Fire delay")]
public float BradleyCanonFireDelay = 0.3f;
[JsonProperty(PropertyName = "Bradley Canon Firerate")]
public float BradleyCanonFireRate = 0.25f;
}
class SettingsBradleyLoot {
[JsonProperty(PropertyName = "Change Values")]
public bool LootSettingsUse = true;
[JsonProperty(PropertyName = "Bradley Max crates after kill")]
public int BradleyMaxCratesAfterKill = 4;
}
class SettingsBradleyMachinegun {
[JsonProperty(PropertyName = "Change Values")]
public bool BMGSettingsUse = true;
[JsonProperty(PropertyName = "Bradley Machinegun Bullet Damage")]
public int BradleyMachinegunBulletDamage = 15;
[JsonProperty(PropertyName = "Bradley Machinegun Firerate")]
public float BradleyMachinegunFirerate = 0.06667f;
}
class SettingsEvent {
[JsonProperty(PropertyName = "Bradley Crew Max Spawn Distance")]
public int CrewSpawnRadius = 10;
[JsonProperty(PropertyName = "Bradley Crew Max Roam Distance")]
public int CrewRoam = 50;
[JsonProperty(PropertyName = "Bradley Crew Body to Bags")]
public bool CrewBodybags = false;
[JsonProperty(PropertyName = "Max throwing range of a Grenade")]
public float MaxThrowingRangeOfAGrenade = 25.0f;
[JsonProperty(PropertyName = "Max Grenades per trigger")]
public int GrenadeAmount = 1;
[JsonProperty(PropertyName = "Max throwing range of a Molotov")]
public float MaxThrowingRangeOfAMolotov = 5.0f;
[JsonProperty(PropertyName = "Max Molotovs per trigger")]
public int MolotovAmount = 1;
[JsonProperty(PropertyName = "Use Firerockets (instead of Molotovs)")]
public bool UseFireRockets = false;
[JsonProperty(PropertyName = "Hard Mode : Throws towards player (Grenades)")]
public bool GrenadeHardMode = true;
[JsonProperty(PropertyName = "Hard Mode : Max Grenades per trigger")]
public int GrenadeAmountHM = 1;
[JsonProperty(PropertyName = "Hard Mode : Max radius of a Grenade on player")]
public float MaxThrowingRangeOfAGrenadeHM = 15.0f;
[JsonProperty(PropertyName = "Hard Mode : Throws towards player (Molotovs)")]
public bool MolotovHardMode = true;
[JsonProperty(PropertyName = "Hard Mode : Max Molotovs per trigger")]
public int MolotovAmountAmountHM = 1;
[JsonProperty(PropertyName = "Hard Mode : Max radius of a Molotov on player")]
public float MaxThrowingRangeOfAMolotovHM = 15.0f;
[JsonProperty(PropertyName = "Napalm Support")]
public bool DropSupportNapalmUse = true;
[JsonProperty(PropertyName = "Napalm Support Area Radius")]
public float DropSupportNapalmRange = 35.0f;
[JsonProperty(PropertyName = "Napalm Support Rocket Amount")]
public int DropSupportNapalmAmount = 3;
[JsonProperty(PropertyName = "Napalm Support Rocket Height")]
public int DropSupportNapalmHeight = 50;
[JsonProperty(PropertyName = "Last Ditch Effort Rocket Amount")]
public int LastDitchEffortNapalmAmount = 5;
[JsonProperty(PropertyName = "Last Ditch Effort Grenade Amount")]
public int LastDitchEffortGrenadeAmount = 5;
[JsonProperty(PropertyName = "Last Ditch Effort MLRS rocket Radius")]
public int LastDitchEffortMLRSRadius = 10;
[JsonProperty(PropertyName = "Last Ditch Effort MLRS rocket Amount")]
public int LastDitchEffortMLRSAmount = 5;
[JsonProperty(PropertyName = "Last Ditch Effort Hard Mode : targets player location")]
public bool LastDitchEffortHardMode = false;
}
class TankCrewDriver {
[JsonProperty(PropertyName = "Tank Driver Health")]
public float TankDriverHealth = 450.0f;
[JsonProperty(PropertyName = "Tank Driver Lifetime (minutes)")]
public float DriverLife = 10f;
[JsonProperty(PropertyName = "Tank Driver Damage multiplier")]
public float TankDriverDamageScale = 1.0f;
[JsonProperty(PropertyName = "Tank Driver Spawn Chance 0-1")]
public float TankDriverSpawnChance = 1.0f;
[JsonProperty(PropertyName = "Tank Driver Spawn Amount")]
public int TankDriverAmount = 1;
[JsonProperty(PropertyName = "Tank Driver Use Outfit")]
public bool UseOutfit1 = true;
[JsonProperty(PropertyName = "Tank Driver Use other gun with outfit")]
public bool UseOutfitG1 = false;
[JsonProperty(PropertyName = "Tank Driver gun short prefabname")]
public string UseOutfitGName1 = "smg.mp5";
[JsonProperty(PropertyName = "Tank Driver gun skin id")]
public ulong UseOutfitGSkin1 = 0;
[JsonProperty(PropertyName = "Tank Driver Use kit (clothing)")]
public bool UseKit1 = false;
[JsonProperty(PropertyName = "Tank Driver Kit ID")]
public string KitName1 = "";
}
class TankCrewGuards {
[JsonProperty(PropertyName = "Tank Guards Health")]
public float TankGuardsHealth = 800;
[JsonProperty(PropertyName = "Tank Guards Lifetime (minutes)")]
public float GuardsLife = 10f;
[JsonProperty(PropertyName = "Tank Guards Damage multiplier")]
public float TankGuardsDamageScale = 1.0f;
[JsonProperty(PropertyName = "Tank Guards Spawn Chance 0-1")]
public float TankGuardsSpawnChance = 1.0f;
[JsonProperty(PropertyName = "Tank Guards Spawn Amount")]
public int TankGuardsAmount = 2;
[JsonProperty(PropertyName = "Tank Guards Use Outfit")]
public bool UseOutfit2 = true;
[JsonProperty(PropertyName = "Tank Guards Use other gun with outfit")]
public bool UseOutfitG2 = false;
[JsonProperty(PropertyName = "Tank Guards gun short prefabname")]
public string UseOutfitGName2 = "smg.mp5";
[JsonProperty(PropertyName = "Tank Guards gun skin id")]
public ulong UseOutfitGSkin2 = 0;
[JsonProperty(PropertyName = "Tank Guards Use kit (clothing)")]
public bool UseKit2 = false;
[JsonProperty(PropertyName = "Tank Guards Kit ID")]
public string KitName2 = "";
}
class TankCrewGunner {
[JsonProperty(PropertyName = "Tank Gunners Health")]
public float TankGunnersHealth = 600;
[JsonProperty(PropertyName = "Tank Gunners Lifetime (minutes)")]
public float GunnersLife = 10f;
[JsonProperty(PropertyName = "Tank Gunners Damage multiplier")]
public float TankGunnersDamageScale = 1.0f;
[JsonProperty(PropertyName = "Tank Gunners Spawn Chance 0-1")]
public float TankGunnersSpawnChance = 1.0f;
[JsonProperty(PropertyName = "Tank Gunners Spawn Amount")]
public int TankGunnersAmount = 2;
[JsonProperty(PropertyName = "Tank Gunners Use Outfit")]
public bool UseOutfit3 = true;
[JsonProperty(PropertyName = "Tank Gunners Use other gun with outfit")]
public bool UseOutfitG3 = false;
[JsonProperty(PropertyName = "Tank Gunners gun short prefabname")]
public string UseOutfitGName3 = "smg.mp5";
[JsonProperty(PropertyName = "Tank Gunners gun skin id")]
public ulong UseOutfitGSkin3 = 0;
[JsonProperty(PropertyName = "Tank Gunners Use kit (clothing)")]
public bool UseKit3 = false;
[JsonProperty(PropertyName = "Tank Gunners Kit ID")]
public string KitName3 = "";
}
private bool LoadConfigVariables() {
try {
configData = Config.ReadObject<ConfigData>();
}
catch {
return false;
}
SaveConf();
return true;
}
protected override void LoadDefaultConfig() {
Puts("Fresh install detected Creating a new config file.");
configData = new ConfigData();
SaveConf();
}
void SaveConf() => Config.WriteObject(configData, true);
#endregion
#region LanguageAPI
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["Prefix"]
= "<color=yellow>[BradleyGuards]</color> ", ["InvalidInput"] = "<color=red>Please enter a valid command!</color>", ["Version"] = "Version : V", ["Info"] = "\n<color=green>Available Commands</color>\n<color=green>/bgrespawncustom</color> : Respawns all killed custom placed bradleys <color=purple>[RustEdit]</color>.", ["BradleyDriver"] = "<color=yellow>Driver : </color>Damnit i just washed it!", ["BradleyDriverBomb"] = "<color=yellow>Driver : </color>Catch this!", ["BradleyGuards"] = "<color=yellow>Guard : </color>Stay away from our Bradley!", ["BradleyGunner"] = "<color=yellow>Gunner : </color>Prepare to die!", ["BradleyGunnerFire"] = "<color=yellow>Gunner : </color>Fire in the hooole!", ["BradleyNapalmSupport"] = "<color=yellow>Operator : </color>Support incomming!",
}
, this);
}
#endregion
#region Commands
[ChatCommand("bginfo")]
void cmdBgInfo(BasePlayer player, string cmd, string[] args) {
Player.Message(player, msg("Version") + this.Version.ToString() + " By : " + this.Author.ToString() , chaticon);
return;
}
#endregion
#region External Hooks
List<ulong> BotReSpawns = new List<ulong>();
void OnBotReSpawnNPCSpawned(ScientistNPC npc, string profilename, string group) => BotReSpawns.Add(npc.userID);
object OnNpcKits(BasePlayer player) {
if (_Crew.Contains(player.userID)) return true;
return null;
}
#endregion
#region Oxide Hooks
private object OnNpcTarget(BasePlayer attacker, BasePlayer target) {
if (attacker != null && _Crew.Contains(attacker.userID) && NPCTarget) {
if (target.IsSleeping() || !target.userID.IsSteamId() || !target is BasePet) return true;
}
if (attacker != null && target != null && _Crew.Contains(target.userID) && NPCTarget) {
return true;
}
return null;
}
private object OnSamSiteTarget(SamSite samsite , MLRSRocket target) {
string ownerid = samsite.OwnerID.ToString();
if ((samsite.staticRespawn && ownerid == "0") && target.skinID == 29051978) return false;
else if ((samsite.staticRespawn && ownerid != "0") && target.skinID == 29051978) return true;
return null;
}
private void OnBradleyApcInitialize(BradleyAPC bradley) {
if (Interface.CallHook("IsTrainBradley", bradley) != null && configData.External.CanTriggerTrain == false) {
return;
}
if (Interface.CallHook("IsConvoyVehicle", bradley) != null && bradley.skinID == 755446 && configData.External.CanTriggerConv == false) {
return;
}
if (Interface.CallHook("CanBradleyTiersEdit", bradley) != null && configData.External.CanTriggerSatdish == false) {
return;
}
if (configData.Bradley.BSettingsUse) {
if (BradleyTiers == false) {
bradley._maxHealth = configData.Bradley.BradleyHealth;
bradley.health = bradley._maxHealth;
}
bradley.viewDistance = configData.Bradley.BradleyMaxFireRange;
bradley.searchRange = configData.Bradley.BradleyMaxFireRange;
bradley.throttle = configData.Bradley.BradleySpeed;
bradley.leftThrottle = bradley.throttle;
bradley.rightThrottle = bradley.throttle;
}
if (configData.BradleyLoot.LootSettingsUse && (!BradleyTiers || bradley.skinID != 755446)) {
bradley.maxCratesToSpawn = configData.BradleyLoot.BradleyMaxCratesAfterKill;
}
if (configData.BradleyMachineGun.BMGSettingsUse) {
bradley.bulletDamage = configData.BradleyMachineGun.BradleyMachinegunBulletDamage;
bradley.coaxFireRate = configData.BradleyMachineGun.BradleyMachinegunFirerate;
}
if (configData.BradleyCanon.BCSettingsUse) {
bradley.topTurretFireRate = configData.BradleyCanon.BradleyCanonFireRate;
bradley.nextTopTurretTime = configData.BradleyCanon.BradleyCanonFireDelay;
}
bradley.SendNetworkUpdate();
}
void OnEntityKill(ScientistNPC npc) {
if (npc == null) return;
if (BotReSpawns.Contains(npc.userID)) BotReSpawns.Remove(npc.userID);
}
private object CanBradleyApcTarget(BradleyAPC bradley, BaseEntity target) {
if (Interface.CallHook("IsTrainBradley" , bradley) != null && configData.External.CanTriggerTrain == false) {
return null;
}
if (Interface.CallHook("IsConvoyVehicle" , bradley) != null && bradley.skinID == 755446 && configData.External.CanTriggerConv == false) {
return null;
}
if (Interface.CallHook("CanBradleyTiersEdit" , bradley) != null && configData.External.CanTriggerSatdish == false) {
return null;
}
var player = target as BasePlayer;
if (player != null) {
if (player is global : : HumanNPC || player.IsSleeping() || player.InSafeZone()) {
if (BotReSpawns.Contains(player.userID)) return null;
return false;
}
Vector3 mainTurretPosition = bradley.mainTurret.transform.position;
if (!(player.IsVisible(mainTurretPosition, bradley.CenterPoint()) || player.IsVisible(mainTurretPosition, player.eyes.position) || player.IsVisible(mainTurretPosition, player.transform.position))) {
return false;
}
return null;
}
return null;
}
void OnEntityDeath(BradleyAPC bradley, HitInfo info) {
if (Interface.CallHook("IsTrainBradley", bradley) != null && configData.External.CanTriggerTrain == false) {
return;
}
if (Interface.CallHook("IsConvoyVehicle", bradley) != null && bradley.skinID == 755446 && configData.External.CanTriggerConv == false) {
return;
}
if (Interface.CallHook("CanBradleyTiersEdit", bradley) != null && configData.External.CanTriggerSatdish == false) {
return;
}
if (Interface.CallHook("IsBradleyDrop" , bradley) != null && configData.External.CanTriggerBradleyDrops == false) {
return;
}
if (plugins.Exists("BradleyEvents") && plugins.Exists("BradleyTiers")) {
Puts("BradleyTiers and Events are running, skipping BG spawns");
return;
}
LogToFile("APCKills", $"{DateTime.Now:h:mm:ss tt}] {info.InitiatorPlayer} killed a BradleyAPC", this);
var KillPosition = bradley.transform.position;
BasePlayer player = info.InitiatorPlayer;
if (UnityEngine.Random.Range(0f, 1f) <= configData.TankCrewDriver.TankDriverSpawnChance) {
for (int i = 0; i < configData.TankCrewDriver.TankDriverAmount; i++) {
SpawnDriver(KillPosition, configData.TankCrewDriver.TankDriverHealth, configData.TankCrewDriver.TankDriverDamageScale);
}
if (player != null && HasPerm(player, ShowKillMessages_Perm)) Player.Message(player, msg("BradleyDriver"), chaticon);
LogToFile("APCKills", $"{DateTime.Now:h:mm:ss tt}] {info.InitiatorPlayer} {configData.TankCrewDriver.TankDriverAmount} Tank Driver(s) got out", this);
}
if (UnityEngine.Random.Range(0f, 1f) <= configData.TankCrewGuards.TankGuardsSpawnChance) {
for (int i = 0; i < configData.TankCrewGuards.TankGuardsAmount; i++) {
SpawnGuards(KillPosition, configData.TankCrewGuards.TankGuardsHealth, configData.TankCrewGuards.TankGuardsDamageScale);
}
if (player != null && HasPerm(player, ShowKillMessages_Perm)) Player.Message(player, msg("BradleyGuards"), chaticon);
LogToFile("APCKills", $"{DateTime.Now:h:mm:ss tt}] {info.InitiatorPlayer} {configData.TankCrewGuards.TankGuardsAmount} Tank Guard(s) got out", this);
}
if (UnityEngine.Random.Range(0f, 1f) <= configData.TankCrewGunner.TankGunnersSpawnChance) {
for (int i = 0; i < configData.TankCrewGunner.TankGunnersAmount; i++) {
SpawnGunner(KillPosition, configData.TankCrewGunner.TankGunnersHealth, configData.TankCrewGunner.TankGunnersDamageScale);
}
if (player != null && HasPerm(player, ShowKillMessages_Perm)) Player.Message(player, msg("BradleyGunner"), chaticon);
LogToFile("APCKills", $"{DateTime.Now:h:mm:ss tt}] {info.InitiatorPlayer} {configData.TankCrewGunner.TankGunnersAmount} Tank Gunner(s) got out", this);
}
Vector3 TargetLocation = bradley.transform.position;
Puts($"{info.InitiatorPlayer} Killed the BradleyApc in grid {GetGrid(TargetLocation)} starting the MiniGame ");
if (configData.Event.LastDitchEffortHardMode) TargetLocation = player.transform.position;
LastDitchEffort(TargetLocation);
}
private void OnEntitySpawned(BaseNetworkable entity) {
var corpse = entity as LootableCorpse;
if (corpse == null) return;
if (_Crew.Contains(corpse.playerSteamID)) {
if (configData.Event.CrewBodybags) timer.Once(0.1f, () => {
if (BetterNpcNames) corpse.containers[1].Clear();
//wipe clothing
if (BetterNpcNames) corpse.containers[2].Clear();
//wipe beltbar
corpse.Kill();
corpse.DropItems();
}
);
_Crew.Remove(corpse.playerSteamID);
}
}
void OnEntityTakeDamage(BradleyAPC bradley, HitInfo info, MolotovCocktail molly) {
BasePlayer player = info.InitiatorPlayer;
try {
if (info.WeaponPrefab == molly) {
info.damageTypes = new DamageTypeList();
info.DoHitEffects = false;
return;
}
if (info == null || !player || BradleyEvents) return;
if (!player) return;
if (Interface.CallHook("IsTrainBradley", bradley) != null && configData.External.CanTriggerTrain == false) {
return;
}
if (Interface.CallHook("IsConvoyVehicle", bradley) != null && bradley.skinID == 755446 && configData.External.CanTriggerConv == false) {
return;
}
if (Interface.CallHook("CanBradleyTiersEdit", bradley) != null && configData.External.CanTriggerSatdish == false) {
return;
}
if (Interface.CallHook("IsBradleyDrop" , bradley) != null && configData.External.CanTriggerBradleyDrops == false) {
return;
}
if (player.userID.IsSteamId()) {
if (UnityEngine.Random.Range(0f, 1f) <= 0.05) {
if (configData.Event.GrenadeHardMode) Spawnbombs(player.transform.position);
else if (!configData.Event.GrenadeHardMode) Spawnbombs(bradley.transform.position);
if (player != null && HasPerm(player, ShowMessages_Perm)) Player.Message(player, msg("BradleyDriverBomb"), chaticon);
}
if (UnityEngine.Random.Range(0f, 1f) <= 0.05) {
if (configData.Event.MolotovHardMode) ThrowMolotov(player.transform.position);
else if (!configData.Event.MolotovHardMode) ThrowMolotov(bradley.transform.position);
if (player != null && HasPerm(player, ShowMessages_Perm)) Player.Message(player, msg("BradleyGunnerFire"), chaticon);
}
if (UnityEngine.Random.Range(0f, 1f) <= 0.01) {
if (configData.Event.DropSupportNapalmUse) {
SpawnSupportNapalm(player.transform.position);
if (player != null && HasPerm(player, ShowMessages_Perm)) Player.Message(player, msg("BradleyNapalmSupport"), chaticon);
}
}
return;
}
}
catch {
}
return;
}
private void OnFireBallDamage(FireBall fire, global : : HumanNPC npc, HitInfo info) {
if(info.Initiator is FireBall && fire != null && _Crew.Contains(npc.userID)) {
info.damageTypes = new DamageTypeList();
info.DoHitEffects = false;
info.damageTypes.ScaleAll(0f);
}
return;
}
object OnEntityTakeDamage(global : : HumanNPC npc, HitInfo info) {
if (!(_Crew.Contains(npc.userID))) return null;
BasePlayer player = info.InitiatorPlayer;
if (player) {
Interface.CallHook("OnBradleyNPCGotHit" , npc, info);
}
if (info.WeaponPrefab is MLRSRocket) {
info.damageTypes = new DamageTypeList();
info.DoHitEffects = false;
info.damageTypes.ScaleAll(0f);
return true;
}
return null;
}
object OnPlayerDeath(global : : HumanNPC npc , HitInfo info) {
if (!(_Crew.Contains(npc.userID))) return null;
BasePlayer player = info.InitiatorPlayer;
if (player) {
Interface.CallHook("OnBradleyNPCDeath" , npc , info);
}
return null;
}
#endregion
#region API
private object IsBradleyNpc(global : : HumanNPC npc) {
if (npc == null) return null;
if (_Crew.Contains(npc.userID)) return true;
return null;
}
#endregion
#region Event Helpers
private void RemoveAllNpc() {
BradleyCrew[] BradleyCrew = UnityEngine.Object.FindObjectsOfType<BradleyCrew>();
if (BradleyCrew != null) {
foreach (BradleyCrew crew in BradleyCrew) UnityEngine.Object.Destroy(crew);
}
}
public static Vector3 CheckPos(Vector3 pos) {
pos.y = TerrainMeta.HeightMap.GetHeight(pos);
NavMeshHit navMeshHit;
if (!NavMesh.SamplePosition(pos, out navMeshHit, 1, 1)) pos = Vector3.zero;
else if (WaterLevel.GetWaterDepth(pos, true, false) > 0) pos = Vector3.zero;
else if (Physics.RaycastAll(navMeshHit.position + new Vector3(0, 100, 0), Vector3.down, 99f, 1235288065).Any()) pos = Vector3.zero;
else pos = new Vector3(navMeshHit.position.x, TerrainMeta.HeightMap.GetHeight(navMeshHit.position), navMeshHit.position.z);
return pos;
}
void SpawnDriver(Vector3 position, float Health , float DamageScale) {
var pos = CheckPos(position);
if (pos == Vector3.zero) return;
int BGSR = configData.Event.CrewSpawnRadius;
int x = UnityEngine.Random.Range(-BGSR, BGSR);
int z = UnityEngine.Random.Range(-BGSR, BGSR);
pos += new Vector3(x, 0, z);
global : : HumanNPC driver = (global : : HumanNPC)GameManager.server.CreateEntity(sci, pos, new Quaternion(), true);
driver.Spawn();
NextTick(() => {
if (driver == null) return;
var mono = driver.gameObject.AddComponent<BradleyCrew>();
mono.SpawnPoint = pos;
driver.startHealth = Health;
//configData.TankCrewDriver.TankDriverHealth
driver.InitializeHealth(Health, Health);
driver.displayName = "Bradley Driver";
if (BetterNpcNames != null) driver.displayName = (BetterNpcNames.Call<string>("GiveName", driver , configData.External.DriverTitle, true, true));
driver.damageScale = DamageScale;
//configData.TankCrewDriver.TankDriverDamageScale
(driver as ScientistNPC).radioChatterType = ScientistNPC.RadioChatterType.NONE;
(driver as ScientistNPC).DeathEffects = new GameObjectRef[0];
if (configData.TankCrewDriver.UseOutfit1) {
var inv_wear = driver.inventory.containerWear;
Item kilt = ItemManager.CreateByName("roadsign.kilt", 1, 2120628865);
Item chestplate = ItemManager.CreateByName("metal.plate.torso", 1, 2105505757);
Item facemask = ItemManager.CreateByName("metal.facemask", 1, 2120628865);
Item hoodie = ItemManager.CreateByName("hoodie", 1, 2080975449);
Item gloves = ItemManager.CreateByName("burlap.gloves", 1, 2090790324);
Item pants = ItemManager.CreateByName("pants", 1, 2080977144);
Item boots = ItemManager.CreateByName("shoes.boots", 1, 2090776132);
driver?.inventory?.Strip();
if (kilt != null) kilt.MoveToContainer(inv_wear);
if (chestplate != null) chestplate.MoveToContainer(inv_wear);
if (facemask != null) facemask.MoveToContainer(inv_wear);
if (hoodie != null) hoodie.MoveToContainer(inv_wear);
if (gloves != null) gloves.MoveToContainer(inv_wear);
if (pants != null) pants.MoveToContainer(inv_wear);
if (boots != null) boots.MoveToContainer(inv_wear);
}
if (configData.TankCrewDriver.UseOutfitG1) {
var shortname = configData.TankCrewDriver.UseOutfitGName1;
var skin = configData.TankCrewDriver.UseOutfitGSkin1;
var inv_belt = driver.inventory.containerBelt;
Item weapon = ItemManager.CreateByName(shortname, 1, skin);
inv_belt.Clear();
if (weapon != null) weapon.MoveToContainer(inv_belt);
}
else {
var inv_belt = driver.inventory.containerBelt;
Item weapon = ItemManager.CreateByName("smg.mp5", 1, 0);
inv_belt.Clear();
if (weapon != null) weapon.MoveToContainer(inv_belt);
}
if (configData.TankCrewDriver.UseKit1) {
object checkKit1 = Kits?.CallHook("GetKitInfo", configData.TankCrewDriver.KitName1);
if (checkKit1 == null) {
PrintWarning($"Kit for Driver ({configData.TankCrewDriver.KitName1}) does not exist - Using a default Scientistsuit.");
}
else {
driver?.inventory?.Strip();
Kits?.Call($"GiveKit", driver, configData.TankCrewDriver.KitName1);
}
}
}
);
if (driver.IsHeadUnderwater()) {
driver.Kill();
return;
}
if (!driver.IsOutside()) {
driver.Kill();
SpawnDriver(pos, Health, DamageScale);
return;
}
_Crew.Add(driver.userID);
timer.Once(configData.TankCrewDriver.DriverLife * 60, () => {
if (driver != null) {
driver.Kill();
_Crew.Remove(driver.userID);
//Puts("Driver [" + driver.userID + "] Died of Natural causes!!!");
}
}
);
}
void SpawnGuards(Vector3 position, float Health, float DamageScale) {
var pos = CheckPos(position);
if (pos == Vector3.zero) return;
int BGSR = configData.Event.CrewSpawnRadius;
int x = UnityEngine.Random.Range(-BGSR, BGSR);
int z = UnityEngine.Random.Range(-BGSR, BGSR);
pos += new Vector3(x, 0, z);
global : : HumanNPC Guard = (global : : HumanNPC)GameManager.server.CreateEntity(sci, pos, new Quaternion(), true);
Guard.Spawn();
NextTick(() => {
if (Guard == null) return;
var mono = Guard.gameObject.AddComponent<BradleyCrew>();
mono.SpawnPoint = pos;
Guard.startHealth = Health;
Guard.InitializeHealth(Health, Health);
Guard.displayName = "Bradley Guard";
if (BetterNpcNames != null) Guard.displayName = (BetterNpcNames.Call<string>("GiveName", Guard, configData.External.GuardTitle, true, true));
Guard.damageScale = DamageScale;
(Guard as ScientistNPC).radioChatterType = ScientistNPC.RadioChatterType.NONE;
(Guard as ScientistNPC).DeathEffects = new GameObjectRef[0];
if (configData.TankCrewGuards.UseOutfit2) {
// ... [Code preview truncated due to length. Full source in attached file] ...