C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Oxide.Core;
using System;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Oxide.Core.Plugins;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
namespace Oxide.Plugins {
[Info("HeliControl", "Shady", "1.3.2", ResourceId = 1348)]
[Description("Tweak various settings of helicopters.")]
class HeliControl : RustPlugin {
#region Config/Init
StoredData lootData = new StoredData();
StoredData2 weaponsData = new StoredData2();
StoredData3 cooldownData = new StoredData3();
StoredData4 spawnsData = new StoredData4();
private float boundary;
private HashSet<BaseHelicopter> BaseHelicopters = new HashSet<BaseHelicopter>();
private HashSet<CH47HelicopterAIController> Chinooks = new HashSet<CH47HelicopterAIController>();
private HashSet<HelicopterDebris> Gibs = new HashSet<HelicopterDebris>();
private HashSet<FireBall> FireBalls = new HashSet<FireBall>();
private HashSet<BaseHelicopter> forceCalled = new HashSet<BaseHelicopter>();
private HashSet<CH47HelicopterAIController> forceCalledCH = new HashSet<CH47HelicopterAIController>();
private HashSet<LockedByEntCrate> lockedCrates = new HashSet<LockedByEntCrate>();
private HashSet<HackableLockedCrate> hackLockedCrates = new HashSet<HackableLockedCrate>();
private Dictionary<BaseHelicopter, int> strafeCount = new Dictionary<BaseHelicopter, int>();
private FieldInfo tooHotUntil = typeof(HelicopterDebris).GetField("tooHotUntil", (BindingFlags.Instance | BindingFlags.NonPublic));
private bool init;
private static System.Random rng = new System.Random();
//used for loot crates
private readonly int groundLayer = LayerMask.GetMask("Terrain", "World", "Default");
private readonly string heliPrefab = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
private readonly string chinookPrefab = "assets/prefabs/npc/ch47/ch47scientists.entity.prefab";
bool DisableHeli;
bool DisableDefaultHeliSpawns;
bool DisableDefaultChinookSpawns;
bool UseCustomLoot;
bool DisableGibs;
bool DisableNapalm;
bool AutoCallIfExists;
bool AutoCallIfExistsCH47;
bool DisableCratesDeath;
bool HelicopterCanShootWhileDying;
bool UseCustomHeliSpawns;
bool UseOldSpawning;
bool UseOldSpawningCH47;
float GlobalDamageMultiplier;
float HeliBulletDamageAmount;
float MainRotorHealth;
float TailRotorHealth;
float BaseHealth;
float HeliSpeed;
float HeliStartSpeed;
float HeliStartLength;
float HeliAccuracy;
float TimeBeforeUnlocking;
float TimeBeforeUnlockingHack;
float TurretFireRate;
float TurretburstLength;
float TurretTimeBetweenBursts;
float TurretMaxRange;
float GibsTooHotLength;
float GibsHealth;
float TimeBetweenRockets;
float MinSpawnTime;
float MinSpawnTimeCH47;
float MaxSpawnTime;
float MaxSpawnTimeCH47;
float RocketDamageBlunt;
float RocketDamageExplosion;
float RocketExplosionRadius;
int MaxLootCrates;
int MaxHeliRockets;
int BulletSpeed;
int LifeTimeMinutes;
int LifeTimeMinutesCH47;
int WaterRequired;
int MaxActiveHelicopters;
int HelicoptersToSpawn;
int ChinooksToSpawn;
Dictionary<string, object> cds => GetConfig("Cooldowns", new Dictionary<string, object>());
Dictionary<string, object> limits => GetConfig("Limits", new Dictionary<string, object>());
[PluginReference]
Plugin Vanish;
private float LastSpawnTimer;
private float LastSpawnTimerCH47;
private DateTime LastTimerStart;
private DateTime LastTimerStartCH47;
private Timer _callTimer;
private Timer _callTimerCH47;
private Timer CallTimer {
get {
return _callTimer;
}
set {
if (_callTimer != null) _callTimer.Destroy();
LastTimerStart = DateTime.UtcNow;
LastSpawnTimer = value.Delay;
_callTimer = value;
}
}
private Timer CallTimerCH47 {
get {
return _callTimerCH47;
}
set {
if (_callTimerCH47 != null) _callTimerCH47.Destroy();
LastTimerStartCH47 = DateTime.UtcNow;
LastSpawnTimerCH47 = value.Delay;
_callTimerCH47 = value;
}
}
private BaseHelicopter TimerHeli;
private CH47HelicopterAIController TimerCH47;
protected override void LoadDefaultConfig() {
Config["Spawning - Disable Helicopter"] = DisableHeli = GetConfig("Spawning - Disable Helicopter", false);
Config["Spawning - Disable Rust's default spawns"] = DisableDefaultHeliSpawns = GetConfig("Spawning - Disable Rust's default spawns", false);
Config["Spawning - Disable CH47 default spawns"] = DisableDefaultChinookSpawns = GetConfig("Spawning - Disable CH47 default spawns", false);
Config["Loot - Use Custom loot spawns"] = UseCustomLoot = GetConfig("Loot - Use Custom loot spawns", false);
Config["Damage - Global damage multiplier"] = GlobalDamageMultiplier = GetConfig("Damage - Global damage multiplier", 1f);
Config["Turrets - Helicopter bullet damage"] = HeliBulletDamageAmount = GetConfig("Turrets - Helicopter bullet damage", 20f);
Config["Misc - Helicopter can shoot while dying"] = HelicopterCanShootWhileDying = GetConfig("Misc - Helicopter can shoot while dying", true);
Config["Health - Main rotor health"] = MainRotorHealth = GetConfig("Health - Main rotor health", 750f);
Config["Health - Tail rotor health"] = TailRotorHealth = GetConfig("Health - Tail rotor health", 375f);
Config["Health - Base Helicopter health"] = BaseHealth = GetConfig("Health - Base Helicopter health", 10000f);
Config["Loot - Max Crates to drop"] = MaxLootCrates = GetConfig("Loot - Max Crates to drop", 4);
Config["Misc - Helicopter speed"] = HeliSpeed = GetConfig("Misc - Helicopter speed", 25f);
Config["Turrets - Helicopter bullet accuracy"] = HeliAccuracy = GetConfig("Turrets - Helicopter bullet accuracy", 2f);
Config["Rockets - Max helicopter rockets"] = MaxHeliRockets = GetConfig("Rockets - Max helicopter rockets", 12);
Config["Spawning - Disable helicopter gibs"] = DisableGibs = GetConfig("Spawning - Disable helicopter gibs", false);
Config["Spawning - Disable helicopter napalm"] = DisableNapalm = GetConfig("Spawning - Disable helicopter napalm", false);
Config["Turrets - Helicopter bullet speed"] = BulletSpeed = GetConfig("Turrets - Helicopter bullet speed", 250);
Config["Loot - Time before unlocking crates"] = TimeBeforeUnlocking = GetConfig("Loot - Time before unlocking crates", -1f);
Config["Loot - Time before unlocking CH47 crates"] = TimeBeforeUnlockingHack = GetConfig("Loot - Time before unlocking CH47 crates", -1f);
Config["Misc - Maximum helicopter life time in minutes"] = LifeTimeMinutes = GetConfig("Misc - Maximum helicopter life time in minutes", 15);
Config["Misc - Maximum CH47 life time in minutes"] = LifeTimeMinutesCH47 = GetConfig("Misc - Maximum helicopter life time in minutes", 15);
Config["Rockets - Time between each rocket in seconds"] = TimeBetweenRockets = GetConfig("Rockets - Time between each rocket in seconds", 0.2f);
Config["Turrets - Turret fire rate in seconds"] = TurretFireRate = GetConfig("Turrets - Fire rate in seconds", 0.125f);
Config["Turrets - Turret burst length in seconds"] = TurretburstLength = GetConfig("Turrets - Burst length in seconds", 3f);
Config["Turrets - Time between turret bursts in seconds"] = TurretTimeBetweenBursts = GetConfig("Turrets - Time between turret bursts in seconds", 3f);
Config["Turrets - Max range"] = TurretMaxRange = GetConfig("Turrets - Max range", 300f);
Config["Rockets - Blunt damage to deal"] = RocketDamageBlunt = GetConfig("Rockets - Blunt damage to deal", 175f);
Config["Rockets - Explosion damage to deal"] = RocketDamageExplosion = GetConfig("Rockets - Explosion damage to deal", 100f);
Config["Rockets - Explosion radius"] = RocketExplosionRadius = GetConfig("Rockets - Explosion radius", 6f);
Config["Gibs - Time until gibs can be harvested in seconds"] = GibsTooHotLength = GetConfig("Gibs - Time until gibs can be harvested in seconds", 480f);
Config["Gibs - Health of gibs"] = GibsHealth = GetConfig("Gibs - Health of gibs", 500f);
Config["Spawning - Automatically call helicopter between min seconds"] = MinSpawnTime = GetConfig("Spawning - Automatically call helicopter between min seconds", 0f);
Config["Spawning - Automatically call helicopter between max seconds"] = MaxSpawnTime = GetConfig("Spawning - Automatically call helicopter between max seconds", 0f);
Config["Spawning - Automatically call CH47 between min seconds"] = MinSpawnTimeCH47 = GetConfig("Spawning - Automatically call CH47 between min seconds", 0f);
Config["Spawning - Automatically call CH47 between max seconds"] = MaxSpawnTimeCH47 = GetConfig("Spawning - Automatically call CH47 between max seconds", 0f);
Config["Spawning - Use static spawning"] = UseOldSpawning = GetConfig("Spawning - Use static spawning", false);
Config["Spawning - Use static spawning for CH47"] = UseOldSpawningCH47 = GetConfig("Spawning - Use static spawning for CH47", false);
Config["Spawning - Automatically call helicopter if one is already flying"] = AutoCallIfExists = GetConfig("Spawning - Automatically call helicopter if one is already flying", false);
Config["Spawning - Automatically call CH47 if one is already flying"] = AutoCallIfExistsCH47 = GetConfig("Spawning - Automatically call CH47 if one is already flying", false);
Config["Spawning - Helicopters to spawn"] = HelicoptersToSpawn = GetConfig("Spawning - Helicopters to spawn", 1);
Config["Spawning - Chinooks to spawn"] = ChinooksToSpawn = GetConfig("Spawning - Chinooks to spawn", 1);
Config["Misc - Water required to extinguish napalm flames"] = WaterRequired = GetConfig("Misc - Water required to extinguish napalm flames", 10000);
Config["Spawning - Use custom helicopter spawns"] = UseCustomHeliSpawns = GetConfig("Spawning - Use custom helicopter spawns", false);
Config["Misc - Helicopter startup speed"] = HeliStartSpeed = GetConfig("Misc - Helicopter startup speed", 25f);
Config["Misc - Helicopter startup length in seconds"] = HeliStartLength = GetConfig("Misc - Helicopter startup length in seconds", 0f);
Config["Misc - Prevent crates from spawning when forcefully killing helicopter"] = DisableCratesDeath = GetConfig("Misc - Prevent crates from spawning when forcefully killing helicopter", true);
Config["Spawning - Max active helicopters"] = MaxActiveHelicopters = GetConfig("Spawning - Max active helicopters", -1);
for (int i = 0; i < 10; i++) {
object outObj;
var cd = "Cooldown." + i;
var limit = "Limit." + i;
var cdCh47 = "cooldown.ch47." + i;
var limitCh47 = "limit.ch47." + i;
if (!cds.TryGetValue(cd, out outObj)) cds[cd] = 86400f;
if (!limits.TryGetValue(limit, out outObj)) limits[limit] = 5;
if (!cds.TryGetValue(cdCh47, out outObj)) cds[cdCh47] = 86400f;
if (!limits.TryGetValue(limitCh47, out outObj)) limits[limitCh47] = 5;
}
Config["Cooldowns"] = cds;
Config["Limits"] = limits;
SaveConfig();
}
void Init() {
cooldownData = Interface.Oxide?.DataFileSystem?.ReadObject<StoredData3>("HeliControlCooldowns") ?? new StoredData3();
LoadDefaultConfig();
Config["Cooldowns"] = cds;
// unsure if needed
Config["Limits"] = limits;
SaveConfig();
string[] perms = {
"callheli", "callheliself", "callhelitarget", "callch47", "callch47self", "callch47target", "killch47", "killheli", "strafe", "update", "destination", "dropcrate", "killnapalm", "killgibs", "unlockcrates", "admin", "ignorecooldown", "ignorelimits", "tpheli", "tpch47", "helispawn", "callmultiple", "callmultiplech47", "nodrop", "nextheli"
}
;
for (int j = 0; j < perms.Length; j++) permission.RegisterPermission("helicontrol." + perms[j], this);
foreach (var limit in limits.Keys) permission.RegisterPermission("helicontrol." + limit, this);
foreach (var cd in cds.Keys) permission.RegisterPermission("helicontrol." + cd, this);
if (HelicopterCanShootWhileDying) {
Unsubscribe(nameof(CanBeTargeted));
Unsubscribe(nameof(OnHelicopterTarget));
Unsubscribe(nameof(CanHelicopterStrafeTarget));
Unsubscribe(nameof(CanHelicopterStrafe));
Unsubscribe(nameof(CanHelicopterTarget));
}
if (!DisableNapalm) Unsubscribe(nameof(CanHelicopterUseNapalm));
AddCovalenceCommand("unlockcrates", "cmdUnlockCrates");
AddCovalenceCommand("tpheli", "cmdTeleportHeli");
AddCovalenceCommand("killheli", "cmdKillHeli");
AddCovalenceCommand("killch47", "cmdKillCH47");
AddCovalenceCommand("dropcrate", "cmdDropCH47Crate");
AddCovalenceCommand("updatehelis", "cmdUpdateHelicopters");
AddCovalenceCommand("strafe", "cmdStrafeHeli");
AddCovalenceCommand("helidest", "cmdDestChangeHeli");
AddCovalenceCommand("killnapalm", "cmdKillFB");
AddCovalenceCommand("killgibs", "cmdKillGibs");
AddCovalenceCommand("nextheli", "cmdNextHeli");
LoadDefaultMessages();
}
protected override void LoadDefaultMessages() {
var messages = new Dictionary<string, string> {
//DO NOT EDIT LANGUAGE FILES HERE! Navigate to oxide\lang
{
"noPerms", "You do not have permission to use this command!"
}
, {
"invalidSyntax", "Invalid Syntax, usage example: {0} {1}"
}
, {
"invalidSyntaxMultiple", "Invalid Syntax, usage example: {0} {1} or {2} {3}"
}
, {
"heliCalled", "Helicopter Inbound!"
}
, {
"helisCalledPlayer", "{0} Helicopter(s) called on: {1}"
}
, {
"entityDestroyed", "{0} {1}(s) were annihilated!"
}
, {
"helisForceDestroyed", "{0} Helicopter(s) were forcefully destroyed!"
}
, {
"heliAutoDestroyed", "Helicopter auto-destroyed because config has it disabled!"
}
, {
"playerNotFound", "Could not find player: {0}"
}
, {
"noHelisFound", "No active helicopters were found!"
}
, {
"cannotBeCalled", "This can only be called on a single Helicopter, there are: {0} active."
}
, {
"strafingOtherPosition", "Helicopter is now strafing {0}'s position."
}
, {
"destinationOtherPosition", "Helicopter's destination has been set to {0}'s position."
}
, {
"IDnotFound", "Could not find player by ID: {0}"
}
, {
"updatedHelis", "{0} helicopters were updated successfully!"
}
, {
"callheliCooldown", "You must wait before using this again! You've waited: {0}/{1}"
}
, {
"invalidCoordinate", "Incorrect argument supplied for {0} coordinate!"
}
, {
"coordinatesOutOfBoundaries", "Coordinates are out of map boundaries!"
}
, {
"callheliLimit", "You've used your daily limit of {0} heli calls!"
}
, {
"unlockedAllCrates", "Unlocked all Helicopter crates!"
}
, {
"teleportedToHeli", "You've been teleported to the ground below the active Helicopter!"
}
, {
"removeAddSpawn", "To remove a Spawn, type: /helispawn remove SpawnName\n\nTo add a Spawn, type: /helispawn add SpawnName -- This will add the spawn on your current position."
}
, {
"addedSpawn", "Added helicopter spawn {0} with the position of: {1}"
}
, {
"spawnExists", "A spawn point with this name already exists!"
}
, {
"noSpawnsExist", "No Helicopter spawns have been created!"
}
, {
"removedSpawn", "Removed Helicopter spawn point: {0}: {1}"
}
, {
"noSpawnFound", "No spawn could be found with that name!"
}
, {
"onlyCallSelf", "You can only call a Helicopter on yourself, try: /callheli {0}"
}
, {
"spawnCommandLiner", "<color=orange>----</color>Spawns<color=orange>----</color>\n"
}
, {
"spawnCommandBottom", "\n<color=orange>----------------</color>"
}
, {
"cantCallTargetOrSelf", "You do not have the permission to call a Helicopter on a target! Try: /callheli"
}
, {
"maxHelis", "Killing helicopter because the maximum active helicopters has been reached"
}
, {
"cmdError", "An error happened while using this command.Please report this to your server administrator."
}
, {
"ch47AlreadyDropped", "This CH47 has already dropped a crate!"
}
, {
"ch47DroppedCrate", "Dropped crate!"
}
, {
"noTimeFound", "No spawn time found for helicopter."
}
, {
"noTimeFoundCH47", "No spawn time found for CH47."
}
, {
"nextHeliSpawn", "Next helicopter spawn: {0}"
}
, {
"nextCH47Spawn", "Next CH47 spawn: {0}"
}
, {
"nextAlreadyActive", "A helicopter is already active."
}
, {
"itemNotFound", "Item not found!"
}
,
}
;
lang.RegisterMessages(messages, this);
}
private string GetMessage(string key, string steamId = null) => lang.GetMessage(key, this, steamId);
void OnServerInitialized() {
timer.Every(10f, () => CheckHelicopter());
foreach(var entity in BaseNetworkable.serverEntities) {
if (entity == null) continue;
var heli = entity as BaseHelicopter;
var ch47 = entity as CH47HelicopterAIController;
var crate = entity as LockedByEntCrate;
var hackCrate = entity as HackableLockedCrate;
var debris = entity as HelicopterDebris;
var fireball = entity as FireBall;
if (heli != null) BaseHelicopters.Add(heli);
if (ch47 != null) Chinooks.Add(ch47);
if (crate != null) lockedCrates.Add(crate);
if (hackCrate != null) hackLockedCrates.Add(hackCrate);
if (debris != null) Gibs.Add(debris);
if (fireball != null && (fireball.ShortPrefabName.Contains("napalm") || fireball.ShortPrefabName.Contains("oilfire"))) FireBalls.Add(fireball);
}
foreach (var heli in BaseHelicopters) {
if (heli == null) continue;
UpdateHeli(heli, false);
}
var eventPrefabs = (DisableDefaultHeliSpawns || DisableDefaultChinookSpawns) ? GameObject.FindObjectsOfType<TriggeredEventPrefab>() : null;
if (DisableDefaultHeliSpawns) {
var heliEvent = eventPrefabs?.Where(p => (p?.targetPrefab?.resourcePath ?? string.Empty).Contains("heli"))?.FirstOrDefault() ?? null;
if (heliEvent != null) {
GameObject.Destroy(heliEvent);
Puts("Disabled default Helicopter spawning.");
}
}
if (DisableDefaultChinookSpawns) {
var heliEvent = eventPrefabs?.Where(p => (p?.targetPrefab?.resourcePath ?? string.Empty).Contains("ch47"))?.FirstOrDefault() ?? null;
if (heliEvent != null) {
GameObject.Destroy(heliEvent);
Puts("Disabled default Chinook spawning.");
}
}
ConVar.PatrolHelicopter.bulletAccuracy = HeliAccuracy;
ConVar.PatrolHelicopter.lifetimeMinutes = LifeTimeMinutes;
if (TimeBeforeUnlockingHack > 0f) HackableLockedCrate.requiredHackSeconds = TimeBeforeUnlockingHack;
if (UseCustomLoot) LoadSavedData();
LoadHeliSpawns();
LoadWeaponData();
boundary = TerrainMeta.Size.x / 2;
CH47Instance = Chinooks?.FirstOrDefault() ?? null;
var rngTime = GetRandomSpawnTime();
var rngTimeCH47 = GetRandomSpawnTime(true);
if (rngTime > 0f) {
if (!UseOldSpawning) CallTimer = timer.Once(rngTime, () => {
if (HeliCount < 1 || AutoCallIfExists) TimerHeli = callHelis(HelicoptersToSpawn, forced : false)?.FirstOrDefault();
}
);
else {
Timer newTimer = null;
newTimer = timer.Every(rngTime, () => {
if (HeliCount < 1 || AutoCallIfExists) callHelis(HelicoptersToSpawn, forced : false);
LastTimerStart = DateTime.UtcNow.AddSeconds(-newTimer.Delay);
LastSpawnTimer = newTimer.Delay * 2;
Puts("Next Helicopter spawn: " + (rngTime >= 60 ? ((rngTime / 60).ToString("N0") + " minutes") : rngTime.ToString("N0") + " seconds"));
}
);
CallTimer = newTimer;
}
Puts("Next Helicopter spawn: " + (rngTime >= 60 ? ((rngTime / 60).ToString("N0") + " minutes") : rngTime.ToString("N0") + " seconds"));
}
if (rngTimeCH47 > 0f) {
if (!UseOldSpawningCH47) CallTimerCH47 = timer.Once(rngTimeCH47, () => {
if (CH47Count < 1 || AutoCallIfExistsCH47) TimerCH47 = callChinooks(HelicoptersToSpawn, forced : false)?.FirstOrDefault();
}
);
else {
Timer newTimer = null;
newTimer = timer.Every(rngTime, () => {
if (CH47Count < 1 || AutoCallIfExistsCH47) callChinooks(ChinooksToSpawn, forced : false);
LastTimerStart = DateTime.UtcNow.AddSeconds(-newTimer.Delay);
LastSpawnTimer = newTimer.Delay * 2;
Puts("Next CH47 spawn: " + (rngTimeCH47 >= 60 ? ((rngTimeCH47 / 60).ToString("N0") + " minutes") : rngTimeCH47.ToString("N0") + " seconds"));
}
);
CallTimerCH47 = newTimer;
}
Puts("Next CH47 spawn: " + (rngTimeCH47 >= 60 ? ((rngTimeCH47 / 60).ToString("N0") + " minutes") : rngTimeCH47.ToString("N0") + " seconds"));
}
init = true;
}
#endregion
#region Hooks
void Unload() {
SaveData3();
SaveData4();
}
void OnServerSave() {
SaveData3();
SaveData4();
}
void OnEntitySpawned(BaseNetworkable entity) {
if (entity == null || !init) return;
var prefabname = entity?.ShortPrefabName ?? string.Empty;
var longprefabname = entity?.PrefabName ?? string.Empty;
if (string.IsNullOrEmpty(prefabname) || string.IsNullOrEmpty(longprefabname)) return;
var ownerID = (entity as BaseEntity)?.OwnerID ?? 0;
if (entity is LockedByEntCrate) lockedCrates.Add(entity as LockedByEntCrate);
if (entity is HackableLockedCrate) hackLockedCrates.Add(entity as HackableLockedCrate);
if (entity is CH47HelicopterAIController) {
var ch = entity as CH47HelicopterAIController;
ch.Invoke(() => {
if (!ch.IsDestroyed) ch.Kill();
}
, (LifeTimeMinutesCH47 * 60));
Chinooks.Add(ch);
CH47Instance = ch;
}
if (entity is FireBall && (entity.ShortPrefabName.Contains("napalm") || entity.ShortPrefabName.Contains("oil"))) FireBalls.Add(entity as FireBall);
if (prefabname.Contains("rocket_heli")) {
var explosion = entity as TimedExplosive;
if (explosion == null || explosion.IsDestroyed) return;
if (MaxHeliRockets < 1) explosion.Kill();
else {
if (MaxHeliRockets > 12 && ownerID == 0) {
var strafeHeli = BaseHelicopters?.Where(p => p != null && !p.IsDestroyed && p.gameObject != null && (p?.GetComponent<PatrolHelicopterAI>()?._currentState ?? PatrolHelicopterAI.aiState.IDLE) == PatrolHelicopterAI.aiState.STRAFE)?.FirstOrDefault() ?? null;
if (strafeHeli == null || strafeHeli.IsDestroyed) return;
var curCount = 0;
if (!strafeCount.TryGetValue(strafeHeli, out curCount)) curCount = (strafeCount[strafeHeli] = 1);
else curCount = (strafeCount[strafeHeli] += 1);
if (curCount >= 12) {
var heliAI = strafeHeli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (heliAI == null) return;
var actCount = 0;
Action fireAct = null;
fireAct = new Action(() => {
if (actCount >= (MaxHeliRockets - 12)) {
InvokeHandler.CancelInvoke(heliAI, fireAct);
return;
}
actCount++;
FireRocket(heliAI);
}
);
InvokeHandler.InvokeRepeating(heliAI, fireAct, TimeBetweenRockets, TimeBetweenRockets);
strafeCount[strafeHeli] = 0;
}
}
else if (MaxHeliRockets < 12 && (HeliInstance.ClipRocketsLeft() > MaxHeliRockets)) {
explosion.Kill();
return;
}
var dmgTypes = explosion?.damageTypes ?? null;
explosion.explosionRadius = RocketExplosionRadius;
if (dmgTypes != null && dmgTypes.Count > 0) {
for (int i = 0; i < dmgTypes.Count; i++) {
var dmg = dmgTypes[i];
if (dmg.type == Rust.DamageType.Blunt) dmg.amount = RocketDamageBlunt;
if (dmg.type == Rust.DamageType.Explosion) dmg.amount = RocketDamageExplosion;
}
}
}
}
if (prefabname == "heli_crate") {
if (UseCustomLoot && lootData?.HeliInventoryLists != null && lootData.HeliInventoryLists.Count > 0) {
var heli_crate = entity as LootContainer;
if (heli_crate == null || heli_crate?.inventory == null) return;
//possible that the inventory is somehow null? not sure
int index;
index = rng.Next(lootData.HeliInventoryLists.Count);
var inv = lootData.HeliInventoryLists[index];
var itemList = heli_crate?.inventory?.itemList?.ToList() ?? null;
if (itemList != null && itemList.Count > 0) for (int i = 0; i < itemList.Count; i++) RemoveFromWorld(itemList[i]);
//completely remove all existing items in crate
for (int i = 0; i < inv.lootBoxContents.Count; i++) {
var itemDef = inv.lootBoxContents[i];
if (itemDef == null) continue;
var amount = (itemDef.amountMin > 0 && itemDef.amountMax > 0) ? UnityEngine.Random.Range(itemDef.amountMin, itemDef.amountMax) : itemDef.amount;
var skinID = 0ul;
ulong.TryParse(itemDef.skinID.ToString(), out skinID);
var def = ItemManager.FindItemDefinition(itemDef.name);
if (def != null) {
var item = ItemManager.Create(def, amount, skinID);
if (item != null && !item.MoveToContainer(heli_crate.inventory)) RemoveFromWorld(item);
//ensure the item is completely removed if we can't move it, so we're not causing issues
}
}
heli_crate.inventory.MarkDirty();
}
if (TimeBeforeUnlocking != -1f) {
var crate2 = entity as LockedByEntCrate;
if (TimeBeforeUnlocking == 0f) UnlockCrate(crate2);
else timer.Once(TimeBeforeUnlocking, () => {
if (entity == null || entity.IsDestroyed || crate2 == null) return;
UnlockCrate(crate2);
}
);
}
}
if (entity is HelicopterDebris) {
var debris = entity as HelicopterDebris;
if (debris == null) return;
if (DisableGibs) {
NextTick(() => {
if (!(entity?.IsDestroyed ?? true)) entity.Kill();
}
);
return;
}
if (GibsHealth != 500f) {
debris.InitializeHealth(GibsHealth, GibsHealth);
debris.SendNetworkUpdate();
}
Gibs.Add(debris);
if (GibsTooHotLength != 480f) tooHotUntil.SetValue(debris, Time.realtimeSinceStartup + GibsTooHotLength);
}
if (entity is BaseHelicopter) {
var isMax = (HeliCount >= MaxActiveHelicopters && MaxActiveHelicopters != -1);
if (DisableHeli || isMax) NextTick(() => {
if (!(entity?.IsDestroyed ?? true)) entity.Kill();
}
);
if (DisableHeli) {
Puts(GetMessage("heliAutoDestroyed"));
return;
}
else if (isMax) {
Puts(GetMessage("maxHelis"));
return;
}
var AIHeli = entity?.GetComponent<PatrolHelicopterAI>() ?? null;
var BaseHeli = entity as BaseHelicopter;
if (AIHeli == null || BaseHeli == null) return;
BaseHelicopters.Add(BaseHeli);
UpdateHeli(BaseHeli, true);
if (UseCustomHeliSpawns && spawnsData?.HelicopterSpawns != null && spawnsData.HelicopterSpawns.Count > 0 && !forceCalled.Contains(BaseHeli)) {
var valCount = spawnsData.HelicopterSpawns.Count;
var rng = UnityEngine.Random.Range(0, valCount);
var pos = spawnsData.HelicopterSpawns[rng].Position;
BaseHeli.transform.position = pos;
AIHeli.transform.position = pos;
PrintWarning("using custom heli spawn: " + rng + ", " + pos);
}
if (HeliStartLength > 0.0f && HeliStartSpeed != HeliSpeed) {
AIHeli.maxSpeed = HeliStartSpeed;
timer.Once(HeliStartLength, () => {
if (AIHeli == null || BaseHeli == null || BaseHeli.IsDead()) return;
AIHeli.maxSpeed = HeliSpeed;
}
);
}
}
}
object CanBeTargeted(BaseCombatEntity entity, MonoBehaviour monoTurret) {
if (!init || entity == null || (entity?.IsDestroyed ?? true) || monoTurret == null) return null;
var aiHeli = (monoTurret as HelicopterTurret)?._heliAI ?? null;
if (aiHeli == null) return null;
var player = entity as BasePlayer;
if (player != null && Vanish != null && (Vanish?.Call<bool>("IsInvisible", player) ?? false)) return null;
if ((aiHeli?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}
object OnHelicopterTarget(HelicopterTurret turret, BaseCombatEntity entity) {
if (turret == null || entity == null) return null;
if ((turret?._heliAI?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}
object CanHelicopterStrafeTarget(PatrolHelicopterAI entity, BasePlayer target) {
if (entity == null || target == null) return null;
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}
object CanHelicopterStrafe(PatrolHelicopterAI entity) {
if (entity == null) return null;
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}
object CanHelicopterTarget(PatrolHelicopterAI entity, BasePlayer player) {
if (entity == null) return null;
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}
object CanHelicopterUseNapalm(PatrolHelicopterAI entity) {
if (entity == null) return null;
return false;
}
void OnEntityKill(BaseNetworkable entity) {
if (entity == null) return;
var name = entity?.ShortPrefabName ?? string.Empty;
var crate = entity as LockedByEntCrate;
var CH47 = entity as CH47HelicopterAIController;
if (crate != null && lockedCrates.Contains(crate)) lockedCrates.Remove(crate);
if (name.Contains("patrolhelicopter") && !name.Contains("gib")) {
var baseHeli = entity?.GetComponent<BaseHelicopter>() ?? null;
if (baseHeli == null) return;
if (BaseHelicopters.Contains(baseHeli)) BaseHelicopters.Remove(baseHeli);
if (forceCalled.Contains(baseHeli)) forceCalled.Remove(baseHeli);
if (!UseOldSpawning && (CallTimer == null || CallTimer.Destroyed) && baseHeli == TimerHeli) {
var rngTime = GetRandomSpawnTime();
CallTimer = timer.Once(rngTime, () => {
if (HeliCount < 1 || AutoCallIfExists) TimerHeli = callHelis(HelicoptersToSpawn, forced : false)?.FirstOrDefault();
}
);
LastTimerStart = DateTime.UtcNow;
LastSpawnTimer = rngTime;
Puts("Next Helicopter spawn: " + (rngTime >= 60 ? ((rngTime / 60).ToString("N0") + " minutes") : rngTime.ToString("N0") + " seconds"));
}
//otherwise, a timer is already firing (or heli is not a timer heli or old spawning is enabled)
}
if (CH47 != null) {
if (Chinooks.Contains(CH47)) Chinooks.Remove(CH47);
if (forceCalledCH.Contains(CH47)) forceCalledCH.Remove(CH47);
if (!UseOldSpawningCH47 && (CallTimerCH47 == null || CallTimerCH47.Destroyed) && CH47 == TimerCH47) {
var rngTime = GetRandomSpawnTime(true);
CallTimerCH47 = timer.Once(rngTime, () => {
if (CH47Count < 1 || AutoCallIfExistsCH47) TimerCH47 = callChinooks(HelicoptersToSpawn, forced : false)?.FirstOrDefault();
}
);
LastTimerStartCH47 = DateTime.UtcNow;
LastSpawnTimerCH47 = rngTime;
Puts("Next CH47 spawn: " + (rngTime >= 60 ? ((rngTime / 60).ToString("N0") + " minutes") : rngTime.ToString("N0") + " seconds"));
}
//otherwise, a timer is already firing (or heli is not a timer heli or old spawning is enabled)
}
if (entity is FireBall || name.Contains("fireball") || name.Contains("napalm")) {
var fireball = entity as FireBall;
if (fireball != null && FireBalls.Contains(fireball)) FireBalls.Remove(fireball);
}
if (entity is HelicopterDebris) {
var debris = entity as HelicopterDebris;
if (debris != null && Gibs.Contains(debris)) Gibs.Remove(debris);
}
}
void OnPlayerAttack(BasePlayer attacker, HitInfo hitInfo) {
if (attacker == null || hitInfo == null || hitInfo.HitEntity == null) return;
var name = hitInfo?.HitEntity?.ShortPrefabName ?? string.Empty;
if (name == "patrolhelicopter") {
if (GlobalDamageMultiplier != 1f && !(GlobalDamageMultiplier < 0)) {
hitInfo?.damageTypes?.ScaleAll(GlobalDamageMultiplier);
return;
}
var shortName = hitInfo?.Weapon?.GetItem()?.info?.shortname ?? string.Empty;
var displayName = hitInfo?.Weapon?.GetItem()?.info?.displayName?.english ?? string.Empty;
var weaponConfig = 0.0f;
if (string.IsNullOrEmpty(shortName)) return;
if (!weaponsData.WeaponList.TryGetValue(shortName, out weaponConfig)) weaponsData.WeaponList.TryGetValue(displayName, out weaponConfig);
if (weaponConfig != 0.0f && weaponConfig != 1.0f) hitInfo?.damageTypes?.ScaleAll(weaponConfig);
}
}
#endregion
#region Main
private void UpdateHeli(BaseHelicopter heli, bool justCreated = false) {
if (heli == null || heli.IsDestroyed || heli.IsDead()) return;
heli.startHealth = BaseHealth;
if (justCreated) heli.InitializeHealth(BaseHealth, BaseHealth);
heli.maxCratesToSpawn = MaxLootCrates;
heli.bulletDamage = HeliBulletDamageAmount;
heli.bulletSpeed = BulletSpeed;
var weakspots = heli.weakspots;
if (weakspots != null && weakspots.Length > 1) //not even sure if this is needed, but may fix some very strange NRE
{
if (justCreated) {
weakspots[0].health = MainRotorHealth;
weakspots[1].health = TailRotorHealth;
}
weakspots[0].maxHealth = MainRotorHealth;
weakspots[1].maxHealth = TailRotorHealth;
}
var heliAI = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (heliAI == null) return;
heliAI.maxSpeed = Mathf.Clamp(HeliSpeed, 0.1f, 125);
heliAI.timeBetweenRockets = Mathf.Clamp(TimeBetweenRockets, 0.1f, 1f);
heliAI.numRocketsLeft = Mathf.Clamp(MaxHeliRockets, 0, 48);
updateTurrets(heliAI);
heli.SendNetworkUpdateImmediate(justCreated);
}
//nearly exact code used by Rust to fire helicopter rockets
private void FireRocket(PatrolHelicopterAI heliAI) {
if (heliAI == null || !(heliAI?.IsAlive() ?? false)) return;
var num1 = 4f;
var strafeTarget = heliAI.strafe_target_position;
if (strafeTarget == Vector3.zero) return;
var vector3 = heliAI.transform.position + heliAI.transform.forward * 1f;
var direction = (strafeTarget - vector3).normalized;
if (num1 > 0.0) direction = Quaternion.Euler(UnityEngine.Random.Range((float)(-(double)num1 * 0.5), num1 * 0.5f), UnityEngine.Random.Range((float)(-(double)num1 * 0.5), num1 * 0.5f), UnityEngine.Random.Range((float)(-(double)num1 * 0.5), num1 * 0.5f)) * direction;
var flag = heliAI.leftTubeFiredLast;
heliAI.leftTubeFiredLast = !flag;
Effect.server.Run(heliAI.helicopterBase.rocket_fire_effect.resourcePath, heliAI.helicopterBase, StringPool.Get(!flag ? "rocket_tube_right" : "rocket_tube_left"), Vector3.zero, Vector3.forward, (Network.Connection)null, true);
var entity = GameManager.server.CreateEntity(!heliAI.CanUseNapalm() ? heliAI.rocketProjectile.resourcePath : heliAI.rocketProjectile_Napalm.resourcePath, vector3, new Quaternion(), true);
if (entity == null) return;
entity.SendMessage("InitializeVelocity", (direction * 1f));
entity.OwnerID = 1337;
//assign ownerID so it doesn't infinitely loop on OnEntitySpawned
entity.Spawn();
}
private BaseHelicopter callHeli(Vector3 coordinates = new Vector3(), bool forced = true) {
var heli = (BaseHelicopter)GameManager.server.CreateEntity(heliPrefab, new Vector3(), new Quaternion(), true);
if (heli == null) return null;
var heliAI = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (heliAI == null) return null;
if (coordinates != Vector3.zero) heliAI.SetInitialDestination(coordinates + new Vector3(0f, 10f, 0f), 0.25f);
if (forced) forceCalled.Add(heliAI.helicopterBase);
heli.Spawn();
return heli;
}
//chinook position setting code is based off of the same code used by patrolhelicopter in order to (try to) get it to spawn out of the map and fly in
private CH47HelicopterAIController callChinook(Vector3 coordinates = new Vector3(), bool forced = true) {
var heli = (CH47HelicopterAIController)GameManager.server.CreateEntity(chinookPrefab, new Vector3(0, 100, 0), new Quaternion(), true);
if (heli == null) return null;
float x = TerrainMeta.Size.x;
float num = coordinates.y + 50f;
var mapScaleDistance = 0.8f;
//high scale for further out distances/positions
var vector3_1 = Vector3Ex.Range(-1f, 1f);
vector3_1.y = 0.0f;
vector3_1.Normalize();
var vector3_2 = vector3_1 * (x * mapScaleDistance);
vector3_2.y = num;
heli.transform.position = vector3_2;
if (forced) forceCalledCH.Add(heli);
heli.Spawn();
if (coordinates != Vector3.zero) timer.Once(1f, () => {
if (heli != null && !heli.IsDestroyed) SetDestination(heli, coordinates + new Vector3(0f, 10f, 0));
}
);
return heli;
}
private List<BaseHelicopter> callHelis(int amount, Vector3 coordinates = new Vector3(), bool forced = true) {
if (amount < 1) return null;
var listHelis = new List<BaseHelicopter>(amount);
for (int i = 0; i < amount; i++) listHelis.Add(callHeli(coordinates, forced));
return listHelis;
}
private List<CH47HelicopterAIController> callChinooks(int amount, Vector3 coordinates = new Vector3(), bool forced = true) {
if (amount < 1) return null;
var listHelis = new List<CH47HelicopterAIController>(amount);
for (int i = 0; i < amount; i++) listHelis.Add(callChinook(coordinates, forced));
return listHelis;
}
BaseHelicopter callCoordinates(Vector3 coordinates) {
var heli = (BaseHelicopter)GameManager.server.CreateEntity(heliPrefab, new Vector3(), new Quaternion(), true);
if (heli == null) return null;
var heliAI = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (heliAI == null) return null;
heliAI.SetInitialDestination(coordinates + new Vector3(0f, 10f, 0f), 0.25f);
forceCalled.Add(heliAI.helicopterBase);
heli.Spawn();
return heli;
}
private void updateTurrets(PatrolHelicopterAI helicopter) {
if (helicopter == null || helicopter.leftGun == null || helicopter.rightGun == null) return;
helicopter.leftGun.fireRate = (helicopter.rightGun.fireRate = TurretFireRate);
helicopter.leftGun.timeBetweenBursts = (helicopter.rightGun.timeBetweenBursts = TurretTimeBetweenBursts);
helicopter.leftGun.burstLength = (helicopter.rightGun.burstLength = TurretburstLength);
helicopter.leftGun.maxTargetRange = (helicopter.rightGun.maxTargetRange = TurretMaxRange);
}
private int killAllHelis(bool isForced = false) {
CheckHelicopter();
var count = 0;
if (BaseHelicopters.Count < 1) return count;
foreach (var helicopter in BaseHelicopters.ToList()) {
if (helicopter == null || helicopter.IsDead()) continue;
if (DisableCratesDeath) helicopter.maxCratesToSpawn = 0;
if (isF
// ... [Code preview truncated due to length. Full source in attached file] ...