using System; using System.Linq; using System.Reflection; using System.Collections.Generic; using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Plugins; using Rust; using UnityEngine; namespace Oxide.Plugins { [Info("BradleyOptions", "Krungh Crow", "2.3.1", ResourceId = 157)] [Description("Bradley Reworked Configurations")] class BradleyOptions : RustPlugin { #region Changelogs and ToDo /************************************************************* * * 2.1.0 : Added Cooldowns (player) between Bradley kills * 2.1.1 : Added Set Health true/false * Fixed Console spamm error * 2.1.2 : Added cooldown left in chat notification (thx RFC1920) * 2.1.3 : Added chatcommand to see remaining cooldown for the player * Added gametip instead of chat message on bradleycooldown command true/false * 2.1.4 : Added ResourceId (157) * 2.1.5 : Patched for BradleyTiers (skipp hp and crate settings) * 2.1.7 : Patched for December 2nd rust update * 2.1.8 : improved ignore npc target checks * 2.2.1 : Added support for Convoy APC (skips the global modifiers for them) * if a Bradley spawns with less then 100hp tries to update it to cfg values * Added (Realistic explosion when Bradley is killed) option * possible fix for cooldowntimer nre if (player == null) * 2.2.2 : Added support for ArmoredTrain * Pre patched for ArmoredTrain plugin update * Cooldowns will not be aplied when taking on Convoy or ArmoredTrain APC * 2.2.3 : Fix for max crates when using any of the BradleyGuards plugins * : Extended external plugin checks for spawning variables * : Fixed when Apc spawns with low health uses the settings of health to try force update this * : Fixed ArmoredTrain apc targeting issue * 2.2.4 : Lowered the range of fireball check * Cooldown fix * 2.2.5 : Patch for dmg nre * 2.3.0 : Patched for ArmoredTrain,Convoy and SatDishEvent (not released) * 2.3.1 : Updated Other event type Bradleys * Fixed cooldowns being added from Other event type bradleys * **************************************************************/ #endregion [PluginReference] Plugin ArmoredTrain, BradleyGuards, BradleyTiers, Convoy, Paratroopers; #region Variables Dictionary CoolDowns = new Dictionary(); const ulong chaticon = 76561199090290915; const string Admin_Perm = "bradleyoptions.Admin"; const string prefix = "[Bradley Options] "; const string MLRS = "assets/content/vehicles/mlrs/rocket_mlrs.prefab"; const string MLRSFX = "assets/content/vehicles/mlrs/effects/pfx_mlrs_rocket_explosion_ground.prefab"; private bool IsBradleyGuardsB; private bool IsBradleyGuardsK; public class CooldownTimer { public Timer timer; public float start; public float countdown; } #endregion #region Configuration void Init() { if (!LoadConfigVariables()) { Puts("Config file issue detected. Please delete file, or check syntax and fix."); return; } permission.RegisterPermission(Admin_Perm, this); } private ConfigData configData; class ConfigData { [JsonProperty(PropertyName = "Bonus")] public BonusSettings Bonus = new BonusSettings(); [JsonProperty(PropertyName = "Spawn checks Custom Maps (Use this only when bradley does not spawn correctly on custom maps!!!")] public SettingsSpawnchecks Spawnchecks = new SettingsSpawnchecks(); [JsonProperty(PropertyName = "Cooldowns")] public SettingsCooldown CooldownChecks = new SettingsCooldown(); [JsonProperty(PropertyName = "Avoid BradleyOptions override settings for other plugins")] public SettingsPluginchecks PluginChecks = new SettingsPluginchecks(); [JsonProperty(PropertyName = "Spawn checks Vanilla Maps (use this only when you are using just the Launchsite!!!")] public SettingsSpawnchecksVanilla SpawnchecksVanilla = new SettingsSpawnchecksVanilla(); [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(); } class BonusSettings { [JsonProperty(PropertyName = "Realistic explosion when Bradley is killed")] public bool RealExplo = true; } class SettingsCooldown { [JsonProperty(PropertyName = "use cooldown between Bradley Kills")] public bool CooldownUse = false; [JsonProperty(PropertyName = "Show Gametip on bradleycooldown command")] public bool CooldownTipUse = true; [JsonProperty(PropertyName = "cooldown between Bradley kills (minutes)")] public int Cooldown = 120; } class SettingsSpawnchecks { [JsonProperty(PropertyName = "Respawn check (Use rustedit for Custom locations")] public bool respawnrustedit = false; [JsonProperty(PropertyName = "Respawn check (Vanilla Launchsite)")] public bool respawnvanilla = false; [JsonProperty(PropertyName = "Respawn check (Launchsite) each x Minutes")] public float respawntimelaunch = 15.0f; [JsonProperty(PropertyName = "Respawn check (all Custom locations) each x Minutes")] public float respawntimerustedit = 15.0f; } class SettingsPluginchecks { [JsonProperty(PropertyName = "Using Only BradleyOptions")] public bool BOCF = true; [JsonProperty(PropertyName = "Using BradleyGuards/ParaTroopers (Umod)")] public bool BGU = false; [JsonProperty(PropertyName = "Using BradleyGuards (Codefling)")] public bool BGCF = false; } class SettingsSpawnchecksVanilla { [JsonProperty(PropertyName = "Respawn Use (Launchsite Bradley ,use only when spawn checks are disabled)")] public bool respawnvanillalaunch = false; [JsonProperty(PropertyName = "Min respawn time (minutes)")] public float respawntimelaunchmin = 5.0f; [JsonProperty(PropertyName = "Max respawn time (minutes)")] public float respawntimelaunchmax = 5.0f; } class SettingsBradleyLoot { [JsonProperty(PropertyName = "Max crates after kill")] public int BradleyMaxCratesAfterKill = 4; [JsonProperty(PropertyName = "Remove Napalm")] public bool Instantfire = false; [JsonProperty(PropertyName = "Remove Gibs")] public bool KillGibs = false; [JsonProperty(PropertyName = "Instantly unlock crates")] public bool InstantUnlock = false; [JsonProperty(PropertyName = "Instantly unlock Gibs")] public bool InstantUnlockGibs = false; } class SettingsBradley { [JsonProperty(PropertyName = "Enabled (will not spawn when false)")] public bool BradleyEnabled = true; [JsonProperty(PropertyName = "Change Health (If only using BradleyOptions)")] public bool BradleyHealthChange = true; [JsonProperty(PropertyName = "Health")] public int BradleyHealth = 2000; [JsonProperty(PropertyName = "Max Fire Range")] public int BradleyMaxFireRange = 100; [JsonProperty(PropertyName = "Throttle Responce")] public float BradleyThrotle = 1.0f; [JsonProperty(PropertyName = "Hostile timer")] public float BradleyMemory = 20.0f; [JsonProperty(PropertyName = "Ignores sleepers")] public bool sleepersafe = true; [JsonProperty(PropertyName = "Ignores admins")] public bool adminsafe = false; } class SettingsBradleyCanon { [JsonProperty(PropertyName = "Canon Fire delay")] public float BradleyCanonFireDelay = 0.3f; [JsonProperty(PropertyName = "Canon Firerate")] public float BradleyCanonFireRate = 0.25f; } class SettingsBradleyMachinegun { [JsonProperty(PropertyName = "Machinegun Bullet Damage")] public int BradleyMachinegunBulletDamage = 15; [JsonProperty(PropertyName = "Machinegun Firerate")] public float BradleyMachinegunFirerate = 0.06667f; [JsonProperty(PropertyName = "Machinegun Burst Duration")] public int BradleyMachinegunBurstDuration = 10; } private bool LoadConfigVariables() { try { configData = Config.ReadObject(); } 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 { ["CooldownMsg"] = "You have {0} minute(s) cooldown left and have to wait to take on the Bradley again!!", ["CooldownTip"] = "You have {0} minute(s) left to take on the Bradley again!!", ["CooldownEnded"] = "Your Cooldown ended you can take on the Bradley again!!", ["InvalidInput"] = "Please enter a valid command!", ["Prefix"] = "[Bradley Options] ", ["Version"] = "\nVersion : V", ["Info"] = "\nAvailable Commands\n/bradley info : Shows info on version/author and commands", ["InfoRespawn"] = "\n/bradley quickrespawn : Respawns the Launchsite bradley.", ["InfoRespawnCustom"] = "\n/bradley custom : Respawns custom bradleys [RustEdit].", ["RespawnAll"] = "Respawned all Bradleys on custom locations [RustEdit]", ["RespawnLaunch"] = "Respawned The Bradley on Launchsite", ["NoPermission"] = "You do not have permission to use that command!", }, this); } #endregion #region Commands [ChatCommand("bradleycooldown")] private void cmdBradleycooldown(BasePlayer player, string command, string[] args) { string prefix = lang.GetMessage("Prefix", this); { if (CoolDowns.ContainsKey(player.UserIDString)) { string timesince = Math.Floor((CoolDowns[player.UserIDString].start + CoolDowns[player.UserIDString].countdown - Time.realtimeSinceStartup) / 60).ToString(); if (configData.CooldownChecks.CooldownTipUse == false) { Player.Message(player, prefix + string.Format(msg($"CooldownMsg", player.UserIDString), timesince), chaticon); return; } else { TIP(player, msg($"You have {timesince} minute(s) left to take on the Bradley again!!"), 20f); return; } return; } else { if (configData.CooldownChecks.CooldownTipUse == false) { Player.Message(player, prefix + string.Format(msg("CooldownEnded", player.UserIDString)), chaticon); return; } else { TIP(player, msg("CooldownEnded"), 20f); return; } return; } } } [ChatCommand("bradley")] private void cmdBradley(BasePlayer player, string command, string[] args) { if (!permission.UserHasPermission(player.UserIDString, Admin_Perm)) { Player.Message(player,prefix + string.Format(msg("NoPermission", player.UserIDString)), chaticon); return; } if (args.Length == 0) { Player.Message(player,prefix + string.Format(msg("InvalidInput", player.UserIDString)), chaticon); } else { if (args[0].ToLower() == "info") { Player.Message(player,prefix + string.Format(msg("Version", player.UserIDString)) + this.Version.ToString() + " By : " + this.Author.ToString() + msg("Info") + msg("InfoRespawn") + msg("InfoRespawnCustom") + msg("\n\nBradleyAPC's : ") + BaseNetworkable.serverEntities.OfType().Count().ToString() + msg(" ") , chaticon); } if (args[0].ToLower() == "custom") { string prefix = lang.GetMessage("Prefix", this); if (permission.UserHasPermission(player.UserIDString, Admin_Perm)) { this.Server.Command("rustedit.apc.respawn"); { Player.Message(player,prefix + string.Format(msg("RespawnAll", player.UserIDString)) + msg(" BradleyAPC's in Total : ") + BaseNetworkable.serverEntities.OfType().Count().ToString() + msg(" ") , chaticon); } } else { Player.Message(player,prefix + string.Format(msg("NoPermission", player.UserIDString)), chaticon); } } if (args[0].ToLower() == "quickrespawn") { string prefix = lang.GetMessage("Prefix", this); if (permission.UserHasPermission(player.UserIDString, Admin_Perm)) { BradleySpawner bradleySpawner = BradleySpawner.singleton; if (bradleySpawner == null) { Debug.LogWarning("No Spawner"); return; } if (bradleySpawner.spawned) return; { Puts("A bradley spawned succesfully at launchsite!"); } bradleySpawner.spawned = null; bradleySpawner.DoRespawn(); Player.Message(player,prefix + string.Format(msg("RespawnLaunch", player.UserIDString)) + msg(" BradleyAPC's in Total : ") + BaseNetworkable.serverEntities.OfType().Count().ToString() + msg(" ") , chaticon); } else { Player.Message(player,prefix + string.Format(msg("NoPermission", player.UserIDString)), chaticon); } } } } #endregion #region Oxide Hooks void OnServerInitialized() { IsBradleyGuardsB = BradleyGuards != null && BradleyGuards.Author.Contains("Bazz3l"); IsBradleyGuardsK = BradleyGuards != null && BradleyGuards.Author.Contains("Krungh"); timer.Once(3f, () => { if (BradleyTiers) { Puts("BradleyTiers installed skipping HP and Crate amount settings!"); } }); if (configData.Spawnchecks.respawnvanilla) { timer.Every(configData.Spawnchecks.respawntimelaunch * 60, () => { BradleySpawner bradleySpawner = BradleySpawner.singleton; if (bradleySpawner == null) { Debug.LogWarning("No Spawner"); return; } if (bradleySpawner.spawned) return; { Puts("A bradley spawned succesfully at launchsite!"); } bradleySpawner.spawned = null; bradleySpawner.DoRespawn(); }); } timer.Every(configData.Spawnchecks.respawntimerustedit * 60, () => { if (configData.Spawnchecks.respawnrustedit) { this.Server.Command("rustedit.apc.respawn"); Puts("A bradley spawn check for custom locations (rustedit) !"); } return; }); if (configData.SpawnchecksVanilla.respawnvanillalaunch) { BradleySpawner.singleton.minRespawnTimeMinutes = configData.SpawnchecksVanilla.respawntimelaunchmin; BradleySpawner.singleton.maxRespawnTimeMinutes = configData.SpawnchecksVanilla.respawntimelaunchmax; } } private void OnBradleyApcInitialize(BradleyAPC bradley)//profiles { if (bradley == null) return; if (Interface.CallHook("IsTrainBradley" , bradley) != null) return; if (Interface.CallHook("IsConvoyVehicle" , bradley) != null) return; if (Interface.CallHook("CanBradleyTiersEdit" , bradley) != null) return; if (configData.PluginChecks.BOCF)//if BradleyOptions sets the stats { if (configData.Bradley.BradleyHealthChange &&(!IsBradleyGuardsK || !IsBradleyGuardsB || !BradleyTiers)) { bradley._maxHealth = configData.Bradley.BradleyHealth; bradley.health = bradley._maxHealth; } bradley.throttle = configData.Bradley.BradleyThrotle; bradley.leftThrottle = bradley.throttle; bradley.rightThrottle = bradley.throttle; if (!BradleyTiers || !IsBradleyGuardsK || !IsBradleyGuardsK ||!Convoy || !ArmoredTrain) { bradley.maxCratesToSpawn = configData.BradleyLoot.BradleyMaxCratesAfterKill; } bradley.viewDistance = configData.Bradley.BradleyMaxFireRange; bradley.searchRange = configData.Bradley.BradleyMaxFireRange; bradley.bulletDamage = configData.BradleyMachineGun.BradleyMachinegunBulletDamage; bradley.coaxFireRate = configData.BradleyMachineGun.BradleyMachinegunFirerate; bradley.coaxBurstLength = configData.BradleyMachineGun.BradleyMachinegunBurstDuration; bradley.topTurretFireRate = configData.BradleyCanon.BradleyCanonFireRate; bradley.nextTopTurretTime = configData.BradleyCanon.BradleyCanonFireDelay; bradley.memoryDuration = configData.Bradley.BradleyMemory; bradley.SendNetworkUpdate(); } //if (configData.PluginChecks.BGCF)//if BradleyGuards from codefling is enabled if (IsBradleyGuardsK && configData.PluginChecks.BGCF)//if BradleyGuards from codefling is enabled { bradley.memoryDuration = configData.Bradley.BradleyMemory; bradley.coaxBurstLength = configData.BradleyMachineGun.BradleyMachinegunBurstDuration; } //if (configData.PluginChecks.BGU)//if Bradleyguards/ParaTroopers from Umod is enabled if (configData.PluginChecks.BGU && (!IsBradleyGuardsB || !Paratroopers))//if Bradleyguards/ParaTroopers from Umod is enabled { bradley.throttle = configData.Bradley.BradleyThrotle; bradley.leftThrottle = bradley.throttle; bradley.rightThrottle = bradley.throttle; bradley.viewDistance = configData.Bradley.BradleyMaxFireRange; bradley.searchRange = configData.Bradley.BradleyMaxFireRange; bradley.bulletDamage = configData.BradleyMachineGun.BradleyMachinegunBulletDamage; bradley.coaxFireRate = configData.BradleyMachineGun.BradleyMachinegunFirerate; bradley.coaxBurstLength = configData.BradleyMachineGun.BradleyMachinegunBurstDuration; bradley.topTurretFireRate = configData.BradleyCanon.BradleyCanonFireRate; bradley.nextTopTurretTime = configData.BradleyCanon.BradleyCanonFireDelay; bradley.memoryDuration = configData.Bradley.BradleyMemory; } if (bradley.health <= 100) { bradley._maxHealth = configData.Bradley.BradleyHealth; bradley.health = bradley._maxHealth; bradley.SendNetworkUpdate(); Puts($"Updated the HP to {bradley.health}/{bradley.health}"); } bradley.SendNetworkUpdate(); if (!configData.Bradley.BradleyEnabled) { Puts($"Spawning disabled in cfg destroyed the APC!"); bradley.Kill(); return; } } private object CanBradleyApcTarget(BradleyAPC bradley, BaseEntity target) { if (Interface.CallHook("IsTrainBradley", bradley) != null) return null; if (Interface.CallHook("IsConvoyVehicle", bradley) != null) return null; if (Interface.CallHook("CanBradleyTiersEdit", bradley) != null) return null; if (target is NPCPlayer || target is global::HumanNPC) return false; var player = target as BasePlayer; if (!player.userID.IsSteamId()) return false; if (player != null || player.userID.IsSteamId()) { 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; } if (configData.Bradley.sleepersafe) { if (player.IsSleeping()) { return false; } } if (configData.Bradley.adminsafe) { if (player.IsAdmin) { return false; } } } return null; } void OnEntityTakeDamage(BradleyAPC bradley, HitInfo info) { try { BasePlayer player = info.InitiatorPlayer; if (Interface.CallHook("IsTrainBradley", bradley) != null) return; if (Interface.CallHook("IsConvoyVehicle", bradley) != null) return; if (Interface.CallHook("CanBradleyTiersEdit", bradley) != null) return; if (configData.CooldownChecks.CooldownUse == true) { if (player != null) { if (CoolDowns.ContainsKey(player.UserIDString)) { string prefix = lang.GetMessage("Prefix", this); string timesince = Math.Floor((CoolDowns[player.UserIDString].start + CoolDowns[player.UserIDString].countdown - Time.realtimeSinceStartup) / 60).ToString(); Player.Message(player, prefix + string.Format(msg($"CooldownMsg", player.UserIDString), timesince), chaticon); info.damageTypes.ScaleAll(0); return; } } return; } } catch { } return; } private void OnEntitySpawned(FireBall fireball) { List gibs = new List(); Vis.Entities(fireball.transform.position, 1f, gibs); if (fireball is MolotovCocktail) return; foreach (var gib in gibs) { if (gib == null || gib.name == null) continue; if (gib.name.Contains("servergibs_bradley")) { NextTick(() => { try { if (!fireball.IsDestroyed && configData.BradleyLoot.Instantfire) fireball.Kill(); } catch { } }); } } } private void OnEntitySpawned(HelicopterDebris debris) { if (debris.name.Contains("servergibs_bradley") && configData.BradleyLoot.InstantUnlockGibs == true) { if (debris == null || debris.name == null) return; NextTick(() => { debris.tooHotUntil = -1; debris.SendNetworkUpdate(); //Puts("Gibs toohot is used"); if (configData.BradleyLoot.KillGibs) { debris.Kill(); } return; }); return; } //Puts("Gibs toohot not used"); return; } private void OnEntityDeath(BradleyAPC apc, HitInfo info) { if (Interface.CallHook("IsTrainBradley" , apc) != null) return; if (Interface.CallHook("IsConvoyVehicle" , apc) != null) return; if (Interface.CallHook("CanBradleyTiersEdit" , apc) != null) return; var pos = apc.transform.position; if(configData.Bonus.RealExplo) { timer.Once(0.5f, () => { BaseEntity RealisticExplosion = GameManager.server.CreateEntity(MLRS, pos + new Vector3(0, 1, 0), new Quaternion(), true); RealisticExplosion.Spawn(); }); } BasePlayer player = info.InitiatorPlayer; if (!player) { return; } if (!CoolDowns.ContainsKey(player.UserIDString)) { string id = player.UserIDString; var Cooldown = new CooldownTimer(); Cooldown.timer = timer.Once((float)configData.CooldownChecks.Cooldown*60, () => { CoolDowns.Remove(id); string prefix = lang.GetMessage("Prefix", this); if (player != null) Player.Message(player, prefix + string.Format(msg("CooldownEnded", player.UserIDString)), chaticon); }); Cooldown.start = Time.realtimeSinceStartup; Cooldown.countdown = configData.CooldownChecks.Cooldown * 60; CoolDowns.Add(id, Cooldown); } if (configData.CooldownChecks.CooldownUse == true) { string prefix = lang.GetMessage("Prefix", this); Player.Message(player, prefix + string.Format(msg("You now have ", player.UserIDString)) + configData.CooldownChecks.Cooldown + msg(" minutes Cooldown till you can attack again!!"), chaticon); } if (configData.BradleyLoot.InstantUnlock) { NextTick( () => UnlockTheCrates(pos)); } return; } #endregion #region helpers private string msg(string key, string id = null) => lang.GetMessage(key, this, id); private void UnlockTheCrates(Vector3 pos) { List crates = new List(); Vis.Entities(pos, 30f, crates); foreach (var crate in crates) { crate.SetLocked(false); } } void TIP(BasePlayer player, string message, float dur) { if (player == null) return; player.SendConsoleCommand("gametip.showgametip", message); timer.Once(dur, () => player?.SendConsoleCommand("gametip.hidegametip")); } #endregion } }