Error while compiling HeliControl: No overload for method 'OnKilled' takes 1 arguments | Line: 1045, Pos: 18

Error while compiling HeliControl: No overload for method 'OnKilled' takes 1 arguments | Line: 1045, Pos: 18

For me too. Same Error. Need Update

here as well, looking for update or way to help someone make update

found this in console

 

Error while compiling HeliControl: The type 'System.Text.StringBuilder' cannot be used as type parameter 'T' in the generic type or method 'Pool.Free(ref T)'. There is no implicit reference conversion from 'System.Text.StringBuilder' to 'Facepunch.Pool.IPooled'. | Line: 905, Pos: 38

^^ Yeah I have that error , anyone know any fixes?

 

Error while compiling HeliControl: The type 'System.Text.StringBuilder' cannot be used as type parameter 'T' in the generic type or method 'Pool.Free<T>(ref T)'. There is no implicit reference conversion from 'System.Text.StringBuilder' to 'Facepunch.Pool.IPooled'. | Line: 905, Pos: 38

using Oxide.Core;
using System;
using System.Collections.Generic;
using UnityEngine;
using Oxide.Core.Plugins;
using System.Text;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using System.Linq;

namespace Oxide.Plugins
{
[Info("HeliControl", "Shady", "1.4.5", ResourceId = 1348)]
[Description("Tweak various settings of helicopters.")]
internal class HeliControl : RustPlugin
{
//Soli Deo gloria
#region Constants
private const uint ROCKET_PREFAB_ID = 129320027;
private const uint AIRBURST_ROCKET_PREFAB_ID = 3253859536;
private const uint NAPALM_ROCKET_PREFAB_ID = 200672762;
private const uint HELI_CRATE_PREFAB_ID = 1314849795;
private const uint NAPALM_FIREBALL_PREFAB_ID = 184893264;
private const uint OIL_FIREBALL_PREFAB_ID = 3550347674;
private const uint CHINOOK_EVENT_PREFAB_ID = 1514383717;
private const uint HELI_EVENT_PREFAB_ID = 3029415845;

private const uint CHINOOK_SCIENTISTS_PREFAB_ID = 1514383717;

private const string HELI_PREFAB = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
private const string CHINOOK_PREFAB = "assets/prefabs/npc/ch47/ch47scientists.entity.prefab";
#endregion
#region Fields
#region Plugin References
[PluginReference]
private readonly Plugin Vanish;
#endregion

private PatrolHelicopterAI HeliInstance
{
get { return PatrolHelicopterAI.heliInstance; }
set { PatrolHelicopterAI.heliInstance = value; }
}

private CH47HelicopterAIController CH47Instance { get; set; }

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 TriggeredEventPrefab[] _eventPrefabs = null;
public TriggeredEventPrefab[] EventPrefabs
{
get
{
if (_eventPrefabs == null)
{
_eventPrefabs = UnityEngine.Object.FindObjectsOfType<TriggeredEventPrefab>();
}

return _eventPrefabs;
}
}

private NetworkableId _timerHeliId;
private CH47HelicopterAIController _timerCH47;

private bool _configChanged = false;
private bool _terrainHookCalled = false;
private bool _useNapalm = false; //not a config option!
private bool _init;


private StoredData _lootData = new StoredData();
private StoredData2 _weaponsData = new StoredData2();
private StoredData3 _cooldownData = new StoredData3();
private StoredData4 _spawnsData = new StoredData4();

private float _boundary;

private readonly HashSet<PatrolHelicopter> _PatrolHelicopters = new HashSet<PatrolHelicopter>();
private readonly HashSet<CH47HelicopterAIController> _chinooks = new HashSet<CH47HelicopterAIController>();
private readonly HashSet<HelicopterDebris> _gibs = new HashSet<HelicopterDebris>();
private readonly HashSet<FireBall> _fireBalls = new HashSet<FireBall>();
private readonly HashSet<PatrolHelicopter> _forceCalled = new HashSet<PatrolHelicopter>();
private readonly HashSet<CH47HelicopterAIController> _forceCalledCh = new HashSet<CH47HelicopterAIController>();
private readonly HashSet<LockedByEntCrate> _lockedCrates = new HashSet<LockedByEntCrate>();
private readonly HashSet<HackableLockedCrate> _hackLockedCrates = new HashSet<HackableLockedCrate>();

private readonly Dictionary<PatrolHelicopter, int> _strafeCount = new Dictionary<PatrolHelicopter, int>();


private static readonly System.Random _rng = new System.Random(); //used for loot crates

private readonly int _groundLayer = LayerMask.GetMask("Terrain", "World", "Default");

#endregion
#region Config Fields


private bool DisableHeli;
private bool DisableDefaultHeliSpawns;
private bool DisableDefaultChinookSpawns;
private bool UseCustomLoot;
private bool DisableGibs;
private bool DisableNapalm;
private bool AutoCallIfExists;
private bool AutoCallIfExistsCH47;
private bool DisableCratesDeath;
private bool HelicopterCanShootWhileDying;
private bool UseCustomHeliSpawns;
private bool UseOldSpawning;
private bool UseOldSpawningCH47;
private bool SpawnHeliOnRestart;
private bool SpawnChinookOnRestart;
private bool SpawnHeliOnTarget;
private float GlobalDamageMultiplier;
private float HeliBulletDamageAmount;
private float MainRotorHealth;
private float TailRotorHealth;
private float BaseHealth;
private float BaseChinookHealth;
private float HeliSpeed;
private float HeliStartSpeed;
private float HeliStartLength;
private float HeliAccuracy;
private float TimeBeforeUnlocking;
private float TimeBeforeUnlockingHack;
private float TurretFireRate;
private float TurretBurstLength;
private float TurretTimeBetweenBursts;
private float TurretMaxRange;
private float GibsTooHotLength;
private float GibsHealth;
private float TimeBetweenRockets;
private float MinSpawnTime;
private float MinSpawnTimeCH47;
private float MaxSpawnTime;
private float MaxSpawnTimeCH47;
private float RocketDamageBlunt;
private float RocketDamageExplosion;
private float RocketExplosionRadius;
private int MaxLootCrates;
private int MaxHeliRockets;
private int BulletSpeed;
private int LifeTimeMinutes;
private int LifeTimeMinutesCH47;
private int MaxActiveHelicopters;
private int HelicoptersToSpawn;
private int ChinooksToSpawn;

private Dictionary<string, float> Cds;

private Dictionary<string, int> Limits;
#endregion




#region Config
protected override void LoadDefaultConfig()
{

//this cooldown and limit garbage is to fix an issue that was causing duplicate entries in the config.
//I hate it, but it was necessary unless I wanted to ask people to reset or clean up their own configs.
var _coolDowns = GetConfig("Cooldowns", new Dictionary<string, object>());

var _limits = GetConfig("Limits", new Dictionary<string, object>());

Cds = new Dictionary<string, float>();
foreach (var kvp in _coolDowns) Cds[kvp.Key.ToLower()] = Convert.ToSingle(kvp.Value);

Limits = new Dictionary<string, int>();
foreach (var kvp in _limits) Limits[kvp.Key.ToLower()] = Convert.ToInt32(kvp.Value);

if (Limits.Count < _limits.Count || Cds.Count < _coolDowns.Count) _configChanged = true;

for (int i = 0; i < 10; i++)
{
float outFloat;
int outInt;

var cd = "cooldown." + i;
var limit = "limit." + i;
var cdCh47 = "cooldown.ch47." + i;
var limitCh47 = "limit.ch47." + i;

if (!Cds.TryGetValue(cd, out outFloat)) Cds[cd] = 86400f;
if (!Limits.TryGetValue(limit, out outInt)) Limits[limit] = 5;

if (!Cds.TryGetValue(cdCh47, out outFloat)) Cds[cdCh47] = 86400f;
if (!Limits.TryGetValue(limitCh47, out outInt)) Limits[limitCh47] = 5;
}

Config["Cooldowns"] = Cds;
Config["Limits"] = Limits;


DisableHeli = GetConfig("Spawning - Disable Helicopter", false);
DisableDefaultHeliSpawns = GetConfig("Spawning - Disable Rust's default spawns", false);
DisableDefaultChinookSpawns = GetConfig("Spawning - Disable CH47 default spawns", false);
UseCustomLoot = GetConfig("Loot - Use Custom loot spawns", false);
GlobalDamageMultiplier = GetConfig("Damage - Global damage multiplier", 1f);
HeliBulletDamageAmount = GetConfig("Turrets - Helicopter bullet damage", 20f);
HelicopterCanShootWhileDying = GetConfig("Misc - Helicopter can shoot while dying", true);
MainRotorHealth = GetConfig("Health - Main rotor health", 750f);
TailRotorHealth = GetConfig("Health - Tail rotor health", 375f);
BaseHealth = GetConfig("Health - Base Helicopter health", 10000f);
BaseChinookHealth = GetConfig("Health - Base CH47 health", 4000f);
MaxLootCrates = GetConfig("Loot - Max Crates to drop", 4);
HeliSpeed = GetConfig("Misc - Helicopter speed", 25f);
HeliAccuracy = GetConfig("Turrets - Helicopter bullet accuracy", 2f);
MaxHeliRockets = GetConfig("Rockets - Max helicopter rockets", 12);
DisableGibs = GetConfig("Spawning - Disable helicopter gibs", false);
DisableNapalm = GetConfig("Spawning - Disable helicopter napalm", false);
BulletSpeed = GetConfig("Turrets - Helicopter bullet speed", 250);
TimeBeforeUnlocking = GetConfig("Loot - Time before unlocking crates", -1f);
TimeBeforeUnlockingHack = GetConfig("Loot - Time before unlocking CH47 crates", -1f);
LifeTimeMinutes = GetConfig("Misc - Maximum helicopter life time in minutes", 15);
LifeTimeMinutesCH47 = GetConfig("Misc - Maximum helicopter life time in minutes", 15);
TimeBetweenRockets = GetConfig("Rockets - Time between each rocket in seconds", 0.2f);
TurretFireRate = GetConfig("Turrets - Turret fire rate in seconds", 0.125f);
TurretBurstLength = GetConfig("Turrets - Turret burst length in seconds", 3f);
TurretTimeBetweenBursts = GetConfig("Turrets - Time between turret bursts in seconds", 3f);
TurretMaxRange = GetConfig("Turrets - Max range", 300f);
RocketDamageBlunt = GetConfig("Rockets - Blunt damage to deal", 175f);
RocketDamageExplosion = GetConfig("Rockets - Explosion damage to deal", 100f);
RocketExplosionRadius = GetConfig("Rockets - Explosion radius", 6f);
GibsTooHotLength = GetConfig("Gibs - Time until gibs can be harvested in seconds", 480f);
GibsHealth = GetConfig("Gibs - Health of gibs", 500f);
MinSpawnTime = GetConfig("Spawning - Automatically call helicopter between min seconds", 0f);
MaxSpawnTime = GetConfig("Spawning - Automatically call helicopter between max seconds", 0f);
MinSpawnTimeCH47 = GetConfig("Spawning - Automatically call CH47 between min seconds", 0f);
MaxSpawnTimeCH47 = GetConfig("Spawning - Automatically call CH47 between max seconds", 0f);
UseOldSpawning = GetConfig("Spawning - Use static spawning", false);
UseOldSpawningCH47 = GetConfig("Spawning - Use static spawning for CH47", false);
SpawnHeliOnRestart = GetConfig("Spawning - Spawn helicopter after restart", false);
SpawnChinookOnRestart = GetConfig("Spawning - Spawn CH47 after restart", false);
AutoCallIfExists = GetConfig("Spawning - Automatically call helicopter if one is already flying", false);
SpawnHeliOnTarget = GetConfig("Spawning - Spawn helicopters directly on target", true);
AutoCallIfExistsCH47 = GetConfig("Spawning - Automatically call CH47 if one is already flying", false);
HelicoptersToSpawn = GetConfig("Spawning - Helicopters to spawn", 1);
ChinooksToSpawn = GetConfig("Spawning - Chinooks to spawn", 1);
UseCustomHeliSpawns = GetConfig("Spawning - Use custom helicopter spawns", false);
HeliStartSpeed = GetConfig("Misc - Helicopter startup speed", 25f);
HeliStartLength = GetConfig("Misc - Helicopter startup length in seconds", 0f);
DisableCratesDeath = GetConfig("Misc - Prevent crates from spawning when forcefully killing helicopter", true);
MaxActiveHelicopters = GetConfig("Spawning - Max active helicopters", -1);
}
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." },
{"tooManyActiveHelis", "The maximum amount of Helicopters active has been reached. Please try again later." },
{"itemNotFound", "Item not found!" },
};
lang.RegisterMessages(messages, this);
}
private string GetMessage(string key, string steamId = null) => lang.GetMessage(key, this, steamId);
#endregion

#region Hooks
#region Init
private void Init()
{
Unsubscribe(nameof(OnEntitySpawned));

_cooldownData = Interface.Oxide?.DataFileSystem?.ReadObject<StoredData3>("HeliControlCooldowns") ?? new StoredData3();

if (!_configChanged) LoadDefaultConfig(); //don't call load again if it was already called because config file didn't exist. tiny optimization

if (_configChanged) SaveConfig(); //config could have been changed after a manual call of loaddefaultconfig, so we check again

LoadDefaultMessages();


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", "dropcrates", "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", nameof(cmdUnlockCrates));
AddCovalenceCommand("tpheli", nameof(cmdTeleportHeli));
AddCovalenceCommand("killheli", nameof(cmdKillHeli));
AddCovalenceCommand("killch47", nameof(cmdKillCH47));
AddCovalenceCommand("dropcrate", nameof(cmdDropCH47Crate));
AddCovalenceCommand("updatehelis", nameof(cmdUpdateHelicopters));
AddCovalenceCommand("strafe", nameof(cmdStrafeHeli));
AddCovalenceCommand("helidest", nameof(cmdDestChangeHeli));
AddCovalenceCommand("killnapalm", nameof(cmdKillFB));
AddCovalenceCommand("killgibs", nameof(cmdKillGibs));
AddCovalenceCommand("nextheli", nameof(cmdNextHeli));

AddCovalenceCommand("hcdebug", nameof(cmdDebug));

}


private void OnTerrainInitialized()
{
try
{
if (DisableDefaultHeliSpawns) ToggleHeliEvent(false); //OnServerInit may be too late to disable the event
if (DisableDefaultChinookSpawns) ToggleCH47Event(false);
}
finally { _terrainHookCalled = true; }
}

private void OnServerInitialized()
{
try
{
_boundary = TerrainMeta.Size.x * 0.75f;
Subscribe(nameof(OnEntitySpawned));

if (ServerMgr.Instance != null) ServerMgr.Instance.InvokeRepeating(CheckHelicopter, 10f, 10f); //ServerMgr.Instance should never be null after server init, but we check anyway

foreach (var entity in BaseNetworkable.serverEntities)
{
if (entity == null) continue;
var heli = entity as PatrolHelicopter;
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)
{
_PatrolHelicopters.Add(heli);
UpdateHeli(heli);
}

if (ch47?.prefabID == CHINOOK_SCIENTISTS_PREFAB_ID)
{
_chinooks.Add(ch47);
UpdateChinook(ch47);
}

if (crate != null) _lockedCrates.Add(crate);
if (hackCrate != null) _hackLockedCrates.Add(hackCrate);
if (debris != null) _gibs.Add(debris);
if (fireball != null && (fireball.prefabID == NAPALM_FIREBALL_PREFAB_ID || fireball.prefabID == OIL_FIREBALL_PREFAB_ID)) _fireBalls.Add(fireball);
}


ConVar.PatrolHelicopter.bulletAccuracy = HeliAccuracy;
ConVar.PatrolHelicopter.lifetimeMinutes = LifeTimeMinutes;
if (TimeBeforeUnlockingHack > 0f) HackableLockedCrate.requiredHackSeconds = TimeBeforeUnlockingHack;

if (UseCustomLoot) LoadLootData();
LoadHeliSpawns();
LoadWeaponData();




foreach (var ch47 in _chinooks)
{
CH47Instance = ch47; //I feel like this is better than LINQ's FirstOrDefault for a hashset. maybe hashsets shouldn't have been used at all. who knows
break;
}


var randomHeliSpawnTime = GetRandomSpawnTime();
var randomChinookSpawnTime = GetRandomSpawnTime(true);

if (randomHeliSpawnTime > 0)
{
var heliSpawnTime = UseOldSpawning ? GetRandomSpawnTime() : GetAdjustedSecondsToSpawn(GetRandomSpawnTime());

CallTimer = GetHeliSpawnTimer(heliSpawnTime);
}

if (randomChinookSpawnTime > 0)
{
var chinookSpawnTime = UseOldSpawningCH47 ? GetRandomSpawnTime(true) : GetAdjustedSecondsToSpawn(GetRandomSpawnTime(true));

CallTimerCH47 = GetChinookSpawnTimer(chinookSpawnTime);
}

if (_terrainHookCalled)
{
if (SpawnHeliOnRestart) callHelis(HelicoptersToSpawn, forced: false);
if (SpawnChinookOnRestart) callChinooks(ChinooksToSpawn, forced: false);
}
else
{
if (DisableDefaultHeliSpawns) ToggleHeliEvent(false); //OnServerInit may be too late to disable the event
if (DisableDefaultChinookSpawns) ToggleCH47Event(false);
}
}
finally { _init = true; }
}
#endregion

private void Unload()
{
if (ServerMgr.Instance != null) ServerMgr.Instance.CancelInvoke(CheckHelicopter);

ToggleHeliEvent(true);
ToggleCH47Event(true);

SaveCooldownData();
SaveSpawnData();
}

/*/
private void OnServerSave()
{
var saveAction = new Action(() =>
{
SaveCooldownData();
SaveSpawnData();
});
if (ServerMgr.Instance != null) ServerMgr.Instance.Invoke(saveAction, 4f); //ServerMgr.Instance should never be null on a server save, but we'll check anyway. We want to delay saving a few seconds because a lot of things are often called on OnServerSave
else saveAction.Invoke();
}/*/

private void OnEntitySpawned(BaseNetworkable entity)
{
if (entity == null || entity.IsDestroyed || entity.gameObject == null) return; //entity should never be destroyed on spawn, but lets do a check anyway
var prefabname = entity?.ShortPrefabName ?? string.Empty;
var longprefabname = entity?.PrefabName ?? string.Empty;
if (string.IsNullOrEmpty(prefabname) || string.IsNullOrEmpty(longprefabname)) return; //another likely impossibility, but lets just check EVERYTHING



var ownerID = (entity as BaseEntity)?.OwnerID ?? 0;

var lockedEntCrate = entity as LockedByEntCrate;
if (lockedEntCrate != null) _lockedCrates.Add(lockedEntCrate);

var hackableCrate = entity as HackableLockedCrate;
if (hackableCrate != null) _hackLockedCrates.Add(hackableCrate);

var ch = entity as CH47HelicopterAIController;
if (ch?.prefabID == CHINOOK_SCIENTISTS_PREFAB_ID && !ch.ShouldLand()) //ShouldLand will be true for oil rig chinooks. we want to ignore those; we don't control them
{
ch.Invoke(() =>
{
if (!ch.IsDestroyed) ch.Kill();
}, LifeTimeMinutesCH47 * 60);

_chinooks.Add(ch);
CH47Instance = ch;

if (UseOldSpawningCH47) CallTimerCH47 = GetChinookSpawnTimer(GetRandomSpawnTime(true));

UpdateChinook(ch, true);
}

var fireBall = entity as FireBall;
if (fireBall != null && (fireBall.prefabID == NAPALM_FIREBALL_PREFAB_ID || fireBall.prefabID == OIL_FIREBALL_PREFAB_ID)) _fireBalls.Add(fireBall);

if (entity.prefabID == ROCKET_PREFAB_ID || entity.prefabID == NAPALM_ROCKET_PREFAB_ID || entity.prefabID == AIRBURST_ROCKET_PREFAB_ID)
{
var explosion = entity as TimedExplosive;
if (explosion == null || explosion.IsDestroyed || explosion.gameObject == null) return; //super ultra extra safe null checking


_useNapalm = explosion.prefabID == NAPALM_ROCKET_PREFAB_ID && explosion.OwnerID != 1337; //is a napalm rocket & was not spawned by plugin (1337 owner id indicates that)


if (MaxHeliRockets < 1) explosion.Kill();
else
{
explosion.explosionRadius = RocketExplosionRadius;
if (MaxHeliRockets > 12 && ownerID == 0)
{
PatrolHelicopter strafeHeli = null;
foreach (var heli in _PatrolHelicopters) //loop through all active helis to find one that's currently strafing
{
if (heli == null || heli.IsDestroyed || heli.gameObject == null || heli.IsDead()) continue; //super ultra extra safe null checking
var state = heli?.GetComponent<PatrolHelicopterAI>()?._currentState ?? PatrolHelicopterAI.aiState.IDLE;
if (state == PatrolHelicopterAI.aiState.STRAFE)
{
strafeHeli = heli;
break;
}
}

if (strafeHeli == null || strafeHeli.IsDestroyed || strafeHeli.gameObject == null || strafeHeli.IsDead()) return; //super ultra extra safe null checking
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 || heliAI.gameObject == null) return; //extra null checking
var actCount = 0;
Action fireAct = null;
fireAct = new Action(() =>
{
if (heliAI == null || heliAI.gameObject == null || 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 != null && HeliInstance.gameObject != null && HeliInstance.ClipRocketsLeft() > MaxHeliRockets)
{
explosion.Kill();
return;
}


var dmgTypes = explosion?.damageTypes ?? null;

if (dmgTypes != null && dmgTypes.Count > 0)
{
for (int i = 0; i < dmgTypes.Count; i++)
{
var dmg = dmgTypes[i];
if (dmg == null) continue; //impossible? who knows. but we're gonna null check it anyway cause that's what you do
if (dmg.type == Rust.DamageType.Blunt) dmg.amount = RocketDamageBlunt;
if (dmg.type == Rust.DamageType.Explosion) dmg.amount = RocketDamageExplosion;
}
}
}
}

if (entity.prefabID == HELI_CRATE_PREFAB_ID)
{
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

var index = _rng.Next(_lootData.HeliInventoryLists.Count);
var inv = _lootData.HeliInventoryLists[index];
if (inv?.lootBoxContents != null && inv.lootBoxContents.Count > 0)
{
if (heli_crate?.inventory?.itemList != null && heli_crate.inventory.itemList.Count > 0)
{
var itemList = new List<Item>(heli_crate.inventory.itemList);
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 def = ItemManager.FindItemDefinition(itemDef.name);
if (def != null)
{
var item = ItemManager.Create(def, amount, itemDef.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 >= 0f)
{
var crate2 = entity as LockedByEntCrate;
if (crate2 != null)
{
if (TimeBeforeUnlocking <= 0f) UnlockCrate(crate2);
else crate2.Invoke(() =>
{
if (entity == null || entity.IsDestroyed || crate2 == null) return;
UnlockCrate(crate2);
}, TimeBeforeUnlocking);
}
}
}

var debris = entity as HelicopterDebris;
if (debris != null)
{
if (DisableGibs || GibsHealth <= 0)
{
NextTick(() => { if (!(entity?.IsDestroyed ?? true)) entity.Kill(); });
return;
}

if (GibsHealth != 500f)
{
debris.InitializeHealth(GibsHealth, GibsHealth);
debris.SendNetworkUpdate();
}

_gibs.Add(debris);
if (GibsTooHotLength != 480f) debris.tooHotUntil = Time.realtimeSinceStartup + GibsTooHotLength;
}

var BaseHeli = entity as PatrolHelicopter;
if (BaseHeli != null)
{
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;
if (AIHeli == null) return;

if (UseOldSpawning) CallTimer = GetHeliSpawnTimer(GetRandomSpawnTime());


_PatrolHelicopters.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;
}

if (HeliStartLength > 0.0f && HeliStartSpeed != HeliSpeed)
{
AIHeli.maxSpeed = HeliStartSpeed;
AIHeli.Invoke(() =>
{
if (AIHeli == null || AIHeli.gameObject == null || BaseHeli == null || BaseHeli.IsDestroyed || BaseHeli.gameObject == null || BaseHeli.IsDead()) return;
AIHeli.maxSpeed = HeliSpeed;
}, HeliStartLength);
}
}
}

private object CanBeTargeted(BaseCombatEntity entity, MonoBehaviour monoTurret) //this hook is unsubscribed if the config option isn't enabled
{
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 && !player.IsDestroyed && player.gameObject != null && !player.IsSleeping() && Vanish != null && (Vanish?.Call<bool>("IsInvisible", player) ?? false)) return null;
if ((aiHeli?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}

private object OnHelicopterTarget(HelicopterTurret turret, BaseCombatEntity entity) //this hook is unsubscribed if the config option isn't enabled
{
if (turret == null || entity == null) return null;
if ((turret?._heliAI?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}

private object CanHelicopterStrafeTarget(PatrolHelicopterAI entity, BasePlayer target) //this hook is unsubscribed if the config option isn't enabled
{
if (entity == null || target == null) return null;
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}

private object CanHelicopterStrafe(PatrolHelicopterAI entity) //this hook is unsubscribed if the config option isn't enabled
{
if (entity == null) return null;
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}

private object CanHelicopterTarget(PatrolHelicopterAI entity, BasePlayer player) //this hook is unsubscribed if the config option isn't enabled
{
if ((entity?._currentState ?? PatrolHelicopterAI.aiState.DEATH) == PatrolHelicopterAI.aiState.DEATH) return false;
return null;
}

private object CanHelicopterUseNapalm(PatrolHelicopterAI entity) //this hook is unsubscribed if the config option isn't enabled
{
return false;
}

private void OnEntityKill(BaseNetworkable entity)
{

var crate = entity as LockedByEntCrate;
var CH47 = entity as CH47HelicopterAIController;
var baseHeli = entity as PatrolHelicopter;

if (crate != null) _lockedCrates.Remove(crate);

if (baseHeli != null)
{
var heliId = baseHeli.net.ID;
_PatrolHelicopters.Remove(baseHeli);
_forceCalled.Remove(baseHeli);

if (!UseOldSpawning && (CallTimer == null || CallTimer.Destroyed) && (_timerHeliId.Value == 0 || heliId == _timerHeliId)) //this weird timer heli ID stuff is a workaround for an issue I just could not fix or figure out. sometimes the timer heli would not be equal after a server restart, so the timer would never ever restart.
{
var rngTime = GetRandomSpawnTime();
if (rngTime > 0) CallTimer = GetHeliSpawnTimer(rngTime);
} //otherwise, a timer is already firing (or heli is not a timer heli or old spawning is enabled)
}

if (CH47?.prefabID == CHINOOK_SCIENTISTS_PREFAB_ID && !CH47.ShouldLand()) //ShouldLand will be true for oil rig chinooks. we want to ignore those; we don't control them
{
_chinooks.Remove(CH47);
_forceCalledCh.Remove(CH47);

if (!UseOldSpawningCH47 && (CallTimerCH47 == null || CallTimerCH47.Destroyed) && CH47 == _timerCH47)
{
var rngTime = GetRandomSpawnTime(true);
if (rngTime > 0) CallTimerCH47 = GetChinookSpawnTimer(rngTime);
} //otherwise, a timer is already firing (or heli is not a timer heli or old spawning is enabled)
}


var fireball = entity as FireBall;
if (fireball != null) _fireBalls.Remove(fireball);

var debris = entity as HelicopterDebris;
if (debris != null) _gibs.Remove(debris);
}

private void OnPlayerAttack(BasePlayer attacker, HitInfo hitInfo)
{
if (attacker == null || hitInfo?.HitEntity == null) return;

if (hitInfo?.HitEntity is 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;

float weaponConfig;
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(PatrolHelicopter 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);
}

private void UpdateChinook(CH47HelicopterAIController chinook, bool justCreated = false)
{
if (chinook == null || chinook.IsDestroyed || chinook.gameObject == null) return;

chinook.startHealth = BaseChinookHealth;
chinook._maxHealth = BaseChinookHealth;

if (justCreated)
{
chinook.InitializeHealth(BaseChinookHealth, BaseChinookHealth);
}
}

//nearly exact code used by Rust to fire helicopter rockets
private void FireRocket(PatrolHelicopterAI heliAI)
{
if (heliAI == null || !(heliAI?.IsAlive() ?? false)) return;


var strafeTarget = heliAI.strafe_target_position;
if (strafeTarget == Vector3.zero) return;

var num1 = 4f;

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)(-num1 * 0.5), num1 * 0.5f), UnityEngine.Random.Range((float)(-num1 * 0.5), num1 * 0.5f), UnityEngine.Random.Range((float)(-num1 * 0.5), num1 * 0.5f)) * direction;

var leftTubeLast = heliAI.leftTubeFiredLast;
heliAI.leftTubeFiredLast = !leftTubeLast;

var sb = Facepunch.Pool.Get<StringBuilder>();
try
{
Effect.server.Run(heliAI.helicopterBase.rocket_fire_effect.resourcePath, heliAI.helicopterBase, StringPool.Get(sb.Clear().Append("rocket_tube_").Append((!leftTubeLast ? "right" : "left")).ToString()), Vector3.zero, Vector3.forward, null, true);
}
finally { Facepunch.Pool.FreeUnmanaged(ref sb); }


var entity = GameManager.server.CreateEntity(!(_useNapalm && heliAI.CanUseNapalm()) ? heliAI.rocketProjectile.resourcePath : heliAI.rocketProjectile_Napalm.resourcePath, vector3, new Quaternion(), true);
if (entity == null)
{
PrintWarning("Failed to create entity on " + nameof(FireRocket) + " !!");
return;
}

var projectile = entity.GetComponent<ServerProjectile>();
if (projectile != null) projectile.InitializeVelocity(direction * projectile.speed);

entity.OwnerID = 1337; //assign ownerID so it doesn't infinitely loop on OnEntitySpawned
entity.Spawn();
}

private PatrolHelicopter callHeli(Vector3 coordinates = new Vector3(), bool forced = true, bool setPositionAfterSpawn = true)
{
var heli = (PatrolHelicopter)GameManager.server.CreateEntity(HELI_PREFAB, new Vector3(), new Quaternion(), true);
if (heli == null)
{
PrintWarning("Failed to create heli prefab on " + nameof(callHeli));
return null;
}

var heliAI = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (heliAI == null)
{
PrintWarning("Failed to get helicopter AI on " + nameof(callHeli));
return null;
}

if (coordinates != Vector3.zero)
{
if (coordinates.y < 225) coordinates.y = 225;
heliAI.SetInitialDestination(coordinates, 0.25f);
if (setPositionAfterSpawn) heli.transform.position = heliAI.transform.position = coordinates;
}

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(CHINOOK_PREFAB, 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)
{
heli.Invoke(() =>
{
if (heli != null && !heli.IsDestroyed) SetDestination(heli, coordinates + new Vector3(0f, 10f, 0)); //worth noting that null checks inside of invokes are probably unnecessary, because the invoke should never happen if the object turned null... but we check anyway.
}, 1f);
}
return heli;
}

private List<PatrolHelicopter> callHelis(int amount, Vector3 coordinates = new Vector3(), bool forced = true, bool setPositionAfterSpawn = true)
{
if (amount < 1) return null;
var listHelis = new List<PatrolHelicopter>(amount);
for (int i = 0; i < amount; i++) listHelis.Add(callHeli(coordinates, forced, setPositionAfterSpawn));
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;
}

private PatrolHelicopter callCoordinates(Vector3 coordinates)
{
var heli = (PatrolHelicopter)GameManager.server.CreateEntity(HELI_PREFAB, 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 (_PatrolHelicopters.Count < 1) return count;

var helis = new List<PatrolHelicopter>(_PatrolHelicopters);
for (int i = 0; i < helis.Count; i++)
{
var helicopter = helis[i];
if (helicopter != null && !helicopter.IsDead())
{
if (DisableCratesDeath) helicopter.maxCratesToSpawn = 0;

if (isForced) helicopter.Kill(); //network kill if forced, else die 'naturally' with explosion
else helicopter.DieInstantly();

count++;
}
}

CheckHelicopter();
return count;
}

private int KillAllChinooks(bool isForced = false)
{
CheckHelicopter();
var count = 0;
if (_chinooks.Count < 1) return count;

var chinooks = new List<CH47HelicopterAIController>(_chinooks);
for (int i = 0; i < chinooks.Count; i++)
{
var ch47 = chinooks[i];
if (ch47 != null && !ch47.IsDestroyed)
{
if (isForced) ch47.Kill(); //network kill if forced, else die 'naturally' with explosion
else ch47.DieInstantly();

count++;
}
}

CheckHelicopter();
return count;
}
#endregion
#region Commands
[ChatCommand("helispawn")]
private void cmdHeliSpawns(BasePlayer player, string command, string[] args)
{
if (!HasPerms(player.UserIDString, "helispawn"))
{
SendNoPerms(player);
return;
}
if (args.Length < 1)
{
var msgSB = new StringBuilder();
for (int i = 0; i < _spawnsData.HelicopterSpawns.Count; i++)
{
var sp = _spawnsData.HelicopterSpawns[i];
msgSB.Append(sp.Name).Append(": ").Append(sp.Position).Append(", ");
}

if (msgSB.Length > 2) msgSB.Length -= 2;
var msg = msgSB.ToString();

if (!string.IsNullOrEmpty(msg)) SendReply(player, GetMessage("spawnCommandLiner", player.UserIDString) + msgSB + GetMessage("spawnCommandBottom", player.UserIDString));
SendReply(player, GetMessage("removeAddSpawn"), player.UserIDString); //this isn't combined with a new line with the above because there is a strange character limitation per-message, so we send two messages
return;
}
var arg0 = args[0];
var spawn = args.Length > 1 ? FindSpawn(args[1]) : null;
if (arg0.Equals("add", StringComparison.OrdinalIgnoreCase) && args.Length > 1)
{
if (spawn == null)
{
var pos = player?.transform?.position ?? Vector3.zero;
if (pos == Vector3.zero) return;
_spawnsData.HelicopterSpawns.Add(new HelicopterSpawn { Position = pos, Name = args[1] });
SendReply(player, string.Format(GetMessage("addedSpawn", player.UserIDString), args[1], pos));
}
else SendReply(player, GetMessage("spawnExists", player.UserIDString));
}
else if (arg0.Equals("remove", StringComparison.OrdinalIgnoreCase) && args.Length > 1)
{
if (_spawnsData?.HelicopterSpawns == null || _spawnsData.HelicopterSpawns.Count < 1)
{
SendReply(player, GetMessage("noSpawnsExist", player.UserIDString));
return;
}
if (spawn != null)
{
var value = spawn.Position;
_spawnsData.HelicopterSpawns.Remove(spawn);
SendReply(player, string.Format(GetMessage("removedSpawn", player.UserIDString), args[1], value));
}
else SendReply(player, GetMessage("noSpawnFound", player.UserIDString));
}
else SendReply(player, string.Format(GetMessage("invalidSyntaxMultiple", player.UserIDString), "/helispawn add", "SpawnName", "/helispawn remove", "SpawnName"));
}

private void cmdUnlockCrates(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "unlockcrates"))
{
SendNoPerms(player);
return;
}
var chCrate = args.Length > 0 ? args[0].Equals("ch47", StringComparison.OrdinalIgnoreCase) : false;
var bothCrates = args.Length > 0 ? args[0].Equals("all", StringComparison.OrdinalIgnoreCase) : false;

if (bothCrates || !chCrate) foreach (var crate in _lockedCrates) UnlockCrate(crate);

if (bothCrates || chCrate) foreach (var crate in _hackLockedCrates) UnlockCrate(crate);

player.Message(GetMessage("unlockedAllCrates", player.Id));
}

private void cmdDebug(IPlayer player, string command, string[] args)
{
if (!player.IsAdmin) return;

var testTimer = GetHeliSpawnTimer(GetRandomSpawnTime());
player.Reply("got test timer: " + testTimer.Delay);
}

private void cmdNextHeli(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "nextheli"))
{
SendNoPerms(player);
return;
}

var nextHeli = GetNextHeliTime();
var nextCh47 = GetNextCH47Time();

if (nextHeli <= TimeSpan.Zero) player.Message(GetMessage("noTimeFound", player.Id));
else
player.Message(string.Format(GetMessage("nextHeliSpawn", player.Id), !(HeliInstance?.isDead ?? true) && !(HeliInstance?.helicopterBase?.IsDestroyed ?? true) ? GetMessage("nextAlreadyActive", player.Id) : ReadableTimeSpan(nextHeli)));

if (nextCh47 <= TimeSpan.Zero) player.Message(GetMessage("noTimeFoundCH47", player.Id));
else
player.Message(string.Format(GetMessage("nextCH47Spawn", player.Id), CH47Instance != null && !CH47Instance.IsDestroyed ? GetMessage("nextAlreadyActive", player.Id) : ReadableTimeSpan(nextCh47)));
}

private void cmdDropCH47Crate(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "dropcrate"))
{
SendNoPerms(player);
return;
}
if (CH47Instance == null || CH47Instance.IsDestroyed || CH47Instance.IsDead())
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
var all = args.Length > 0 && args[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (CH47Count > 1 && !all)
{
player.Message(string.Format(GetMessage("cannotBeCalled", player.Id), HeliCount.ToString("N0")));
return;
}
if (all) foreach (var ch47 in _chinooks) { if (ch47.CanDropCrate()) ch47.DropCrate(); }
else
{
if (!CH47Instance.CanDropCrate())
{
player.Message(GetMessage("ch47AlreadyDropped", player.Id));
return;
}
CH47Instance.DropCrate();
}
player.Message(GetMessage("ch47DroppedCrate", player.Id));
}


private void cmdTeleportHeli(IPlayer player, string command, string[] args)
{
if (player.IsServer) return;

var ply = player?.Object as BasePlayer;
if (ply == null || ply.IsDestroyed || ply.IsDead() || !ply.IsConnected || ply.IsSleeping()) return;

var tpPos = Vector3.zero;
var tpCh47 = args.Length > 0 ? args[0].Equals("ch47", StringComparison.OrdinalIgnoreCase) : false;
if (!tpCh47)
{
if (!HasPerms(player.Id, "tpheli"))
{
SendNoPerms(player);
return;
}
if (HeliInstance == null || HeliInstance?.transform == null)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
if (HeliCount > 1)
{
player.Message(string.Format(GetMessage("cannotBeCalled", player.Id), HeliCount.ToString("N0")));
return;
}
tpPos = HeliInstance.transform.position;
}
else
{
if (!HasPerms(player.Id, "tpch47"))
{
SendNoPerms(player);
return;
}
if (CH47Instance == null || CH47Instance?.transform == null)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
if (HeliCount > 1)
{
player.Message(string.Format(GetMessage("cannotBeCalled", player.Id), HeliCount.ToString("N0")));
return;
}
tpPos = CH47Instance.transform.position;
}

if (tpPos == Vector3.zero)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
TeleportPlayer(ply, GetGround(tpPos));
player.Message(GetMessage("teleportedToHeli", player.Id));
}

private object CanPlayerCallHeli(BasePlayer player, bool ch47 = false)
{
if (player == null) return null;
var permMsg = GetNoPerms(player.UserIDString);
var callPerm = ch47 ? "callch47" : "callheli";
var callMult = ch47 ? "callmultiplech47" : "callmultiple";
var cooldownTime = GetLowestCooldown(player, ch47);
var limit = GetHighestLimit(player, ch47);
var now = DateTime.Now;
var today = now.ToString("d");
var cdd = GetCooldownInfo(player.userID);
if (cdd == null)
{
cdd = new CooldownInfo(player);
if (_cooldownData?.cooldownList != null) _cooldownData.cooldownList.Add(cdd);
}
var timesCalled = ch47 ? cdd.TimesCalledCH47 : cdd.TimesCalled;
var lastCall = ch47 ? cdd.LastCallDayCH47 : cdd.LastCallDay;
var coolTime = ch47 ? cdd.CooldownTimeCH47 : cdd.CooldownTime;
if (limit < 1 && !IgnoreLimits(player) && !HasPerms(player.UserIDString, callPerm)) return permMsg;
if (!IgnoreLimits(player) && limit > 0)
{
if (timesCalled >= limit && today == lastCall) return string.Format(GetMessage("callheliLimit", player.UserIDString), limit);
else if (today != lastCall)
{
if (ch47) cdd.TimesCalledCH47 = 0;
else cdd.TimesCalled = 0;
}
}
if (!IgnoreCooldown(player) && cooldownTime > 0.0f && !string.IsNullOrEmpty(coolTime))
{
DateTime cooldownDT;
if (!DateTime.TryParse(coolTime, out cooldownDT))
{
PrintWarning("An error has happened while trying to parse date time ''" + coolTime + "''! Report this issue on plugin thread.");
return GetMessage("cmdError", player.UserIDString);
}
var diff = now - cooldownDT;
if (diff.TotalSeconds < cooldownTime)
{
var cooldownDiff = TimeSpan.FromSeconds(cooldownTime);
var waitedString = ReadableTimeSpan(diff);
var timeToWait = ReadableTimeSpan(cooldownDiff);
return string.Format(GetMessage("callheliCooldown", player.UserIDString), waitedString, timeToWait);
}
}
if ((ch47 ? CH47Count : HeliCount) > 0 && !HasPerms(player.UserIDString, callMult)) return string.Format(GetMessage("cannotBeCalled", player.UserIDString), ch47 ? CH47Count : HeliCount);
if (!ch47 && MaxActiveHelicopters >= 0 && (_PatrolHelicopters.Count + 1) > MaxActiveHelicopters) return string.Format(GetMessage("tooManyActiveHelis", player.UserIDString));

return null;
}


[ChatCommand("callheli")]
private void cmdCallToPlayer(BasePlayer player, string command, string[] args)
{
var argsStr = args.Length > 0 ? string.Join(" ", args) : string.Empty;
try
{
var canCall = CanPlayerCallHeli(player) as string;
if (!string.IsNullOrEmpty(canCall))
{
SendReply(player, canCall);
return;
}

var now = DateTime.Now;
var cdd = GetCooldownInfo(player.userID);
if (cdd == null)
{
cdd = new CooldownInfo(player);
_cooldownData.cooldownList.Add(cdd);
}

if (args.Length == 0)
{
if (!HasPerms(player.UserIDString, "helicontrol.callheli"))
{
SendReply(player, GetNoPerms(player.UserIDString));
return;
}
var newHeli = callHeli();
if (!HasPerms(player.UserIDString, "helicontrol.dropcrates")) newHeli.maxCratesToSpawn = 0;
SendReply(player, GetMessage("heliCalled", player.UserIDString));
cdd.CooldownTime = now.ToString();
cdd.LastCallDay = now.ToString("d");
cdd.TimesCalled += 1;
return;
}
var ID = 0ul;
var target = ulong.TryParse(args[0], out ID) ? FindPlayerByID(ID) : FindPlayerByPartialName(args[0]);
if (target == null)
{
SendReply(player, string.Format(GetMessage("playerNotFound", player.UserIDString), args[0]));
return;
}

if (target != null && HasPerms(player.UserIDString, "callheliself") && !HasPerms(player.UserIDString, "callhelitarget") && target != player)
{
SendReply(player, string.Format(GetMessage("onlyCallSelf", player.UserIDString), player.displayName));
return;
}
if (target != null && !HasPerms(player.UserIDString, "callheliself") && !HasPerms(player.UserIDString, "callhelitarget"))
{
SendReply(player, GetMessage("cantCallTargetOrSelf", player.UserIDString));
return;
}

var num = 1;
if (args.Length == 2 && HasPerms(player.UserIDString, "callheli") && !int.TryParse(args[1], out num)) num = 1;

var newHelis = callHelis(num, target.transform.position, setPositionAfterSpawn: SpawnHeliOnTarget);
if (newHelis.Count > 0 && !permission.UserHasPermission(player.UserIDString, "helicontrol.dropcrates")) for (int i = 0; i < newHelis.Count; i++) newHelis[i].maxCratesToSpawn = 0;
SendReply(player, string.Format(GetMessage("helisCalledPlayer", player.UserIDString), num, target.displayName));
cdd.CooldownTime = now.ToString();
cdd.TimesCalled += 1;
cdd.LastCallDay = now.ToString("d");
}
catch (Exception ex)
{
var errorMsg = GetMessage("cmdError", player.UserIDString);
if (!string.IsNullOrEmpty(errorMsg)) SendReply(player, errorMsg);
PrintError("Error while using /callheli with args: " + argsStr + System.Environment.NewLine + ex.ToString());
}
}

[ChatCommand("callch47")]
private void cmdCallCH47(BasePlayer player, string command, string[] args)
{
var canCall = CanPlayerCallHeli(player, true) as string;
if (!string.IsNullOrEmpty(canCall))
{
SendReply(player, canCall);
return;
}
var now = DateTime.Now;
var cdd = GetCooldownInfo(player.userID);
if (cdd == null)
{
cdd = new CooldownInfo(player);
_cooldownData.cooldownList.Add(cdd);
}

if (args.Length < 1)
{
callChinook();
SendReply(player, GetMessage("heliCalled", player.UserIDString));
cdd.CooldownTimeCH47 = now.ToString();
cdd.LastCallDayCH47 = now.ToString("d");
cdd.TimesCalledCH47 += 1;
return;
}
ulong ID;
var target = ulong.TryParse(args[0], out ID) ? FindPlayerByID(ID) : FindPlayerByPartialName(args[0]);
if (target == null)
{
SendReply(player, string.Format(GetMessage("playerNotFound", player.UserIDString), args[0]));
return;
}
if (target != null && HasPerms(player.UserIDString, "callch47self") && !HasPerms(player.UserIDString, "callch47target") && target != player)
{
SendReply(player, string.Format(GetMessage("onlyCallSelf", player.UserIDString), player.displayName));
return;
}
if (target != null && !HasPerms(player.UserIDString, "callch47self") && !HasPerms(player.UserIDString, "callch47target"))
{
SendReply(player, GetMessage("cantCallTargetOrSelf", player.UserIDString));
return;
}
var num = 1;
if (args.Length == 2 && HasPerms(player.UserIDString, "callch47") && !int.TryParse(args[1], out num)) num = 1;

callChinooks(num, target.transform.position);
SendReply(player, string.Format(GetMessage("helisCalledPlayer", player.UserIDString), num, target.displayName));
cdd.CooldownTimeCH47 = now.ToString();
cdd.TimesCalledCH47 += 1;
cdd.LastCallDayCH47 = now.ToString("d");
}


private void cmdKillHeli(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "killheli"))
{
SendNoPerms(player);
return;
}
var forced = args.Length > 0 ? args[0].Equals("forced", StringComparison.OrdinalIgnoreCase) : false;
var numKilled = KillAllHelis(forced);
player.Message(string.Format(GetMessage(forced ? "helisForceDestroyed" : "entityDestroyed", player.Id), numKilled.ToString("N0"), "helicopter"));
}

private void cmdKillCH47(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "killch47"))
{
SendNoPerms(player);
return;
}
var forced = args.Length > 0 ? args[0].Equals("forced", StringComparison.OrdinalIgnoreCase) : false;
var numKilled = KillAllChinooks(forced);
player.Message(string.Format(GetMessage(forced ? "helisForceDestroyed" : "entityDestroyed", player.Id), numKilled.ToString("N0"), "helicopter"));
}

private void cmdUpdateHelicopters(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "update"))
{
SendNoPerms(player);
return;
}
CheckHelicopter();
if (HeliCount < 1 && CH47Count < 1)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
var count = 0;

foreach (var helicopter in _PatrolHelicopters)
{
if (helicopter == null) continue;
UpdateHeli(helicopter, false);
count++;
}

foreach (var ch47 in _chinooks)
{
if (ch47 == null) continue;
UpdateChinook(ch47);
count++;
}

player.Message(string.Format(GetMessage("updatedHelis", player.Id), count));
}


private void cmdStrafeHeli(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "strafe"))
{
SendNoPerms(player);
return;
}
if (HeliCount < 1)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
var isAll = args.Length > 0 && args[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (HeliCount > 1 && !isAll)
{
player.Message(string.Format(GetMessage("cannotBeCalled", player.Id), HeliCount));
return;
}
if (args.Length < (isAll ? 2 : 1))
{
player.Message(string.Format(GetMessage("invalidSyntax", player.Id), "/strafe", "<player name>"));
return;
}

var findArg = isAll ? args[1] : args[0];
var target = FindPlayerByPartialName(findArg);
ulong ID;
if (ulong.TryParse(findArg, out ID)) target = FindPlayerByID(ID);
if (target == null)
{
player.Message(string.Format(GetMessage("playerNotFound", player.Id), findArg));
return;
}
if (isAll)
{
foreach (var heli in _PatrolHelicopters)
{
var ai = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (ai != null) StartStrafe(ai, target, ai.CanUseNapalm());
}
}
else StartStrafe(HeliInstance, target, HeliInstance.CanUseNapalm());
player.Message(string.Format(GetMessage("strafingOtherPosition", player.Id), target.displayName));
}


private void cmdDestChangeHeli(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "destination") && !HasPerms(player.Id, "ch47destination"))
{
SendNoPerms(player);
return;
}
if (args.Length < 1)
{
player.Message(string.Format(GetMessage("invalidSyntax", player.Id), "/strafe", "<player name>"));
return;
}
var isCh47 = args.Last().Equals("ch47", StringComparison.OrdinalIgnoreCase);
if ((isCh47 && !HasPerms(player.Id, "ch47destination")) || (!isCh47 && !HasPerms(player.Id, "destination")))
{
SendNoPerms(player);
return;
}
if (isCh47 && CH47Count < 1)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}
else if (!isCh47 && HeliCount < 1)
{
player.Message(GetMessage("noHelisFound", player.Id));
return;
}

var isAll = args.Length > 0 && args[0].Equals("all", StringComparison.OrdinalIgnoreCase);
if (isAll && args.Length < 2)
{
player.Message(string.Format(GetMessage("invalidSyntax", player.Id), "/strafe", "<player name>"));
return;
}

if ((isCh47 && CH47Count > 1 || !isCh47 && HeliCount > 1) && !isAll)
{
player.Message(string.Format(GetMessage("cannotBeCalled", player.Id), isCh47 ? CH47Count : HeliCount));
return;
}

var findArg = isAll ? args[1] : args[0];
var target = FindPlayerByPartialName(findArg);
ulong ID;
if (ulong.TryParse(findArg, out ID)) target = FindPlayerByID(ID);
if (target == null)
{
player.Message(string.Format(GetMessage("playerNotFound", player.Id), findArg));
return;
}
var targPos = target?.transform?.position ?? Vector3.zero;
var newY = GetGround(targPos).y + 10f;
if (newY > targPos.y) targPos.y = newY;
if (isAll)
{
if (!isCh47)
{
foreach (var heli in _PatrolHelicopters)
{
var ai = heli?.GetComponent<PatrolHelicopterAI>() ?? null;
if (ai != null) SetDestination(ai, targPos);
}
}
else foreach (var ch47 in _chinooks) SetDestination(ch47, targPos);
}
else
{
if (!isCh47) SetDestination(HeliInstance, targPos);
else SetDestination(CH47Instance, targPos);
}
player.Message(string.Format(GetMessage("destinationOtherPosition", player.Id), target.displayName));
}


private void cmdKillFB(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "killnapalm"))
{
SendNoPerms(player);
return;
}
player.Message(string.Format(GetMessage("entityDestroyed", player.Id), KillAllFireballs().ToString("N0"), "fireball"));
}


private void cmdKillGibs(IPlayer player, string command, string[] args)
{
if (!HasPerms(player.Id, "killgibs"))
{
SendNoPerms(player);
return;
}
player.Message(string.Format(GetMessage("entityDestroyed", player.Id), KillAllGibs().ToString("N0"), "helicopter gib"));
}


[ConsoleCommand("callheli")]
private void consoleCallHeli(ConsoleSystem.Arg arg)
{
var player = arg?.Player() ?? null;
if (player != null && !HasPerms(player.UserIDString, "callheli"))
{
SendNoPerms(player);
return;
}
var userIDString = player?.UserIDString ?? string.Empty;
var noDrop = (player != null) ? !HasPerms(player.UserIDString, "helicontrol.dropcrates") : false;
List<PatrolHelicopter> newHelis;

if (arg.Args == null || arg?.Args?.Length < 1)
{
var newHeli = callHeli();
if (newHeli != null && noDrop) newHeli.maxCratesToSpawn = 0;
SendReply(arg, GetMessage("heliCalled", userIDString));
return;
}

var isPos = arg.Args[0].Equals("pos", StringComparison.OrdinalIgnoreCase);
if (isPos && arg.Args.Length < 4)
{
SendReply(arg, "You must supply 3 args for coordinates!");
return;
}

if (isPos)
{
var coords = default(Vector3);
var callNum = 1;
if (!float.TryParse(arg.Args[1], out coords.x))
{
SendReply(arg, string.Format(GetMessage("invalidCoordinate", userIDString), "X"));
return;
}
if (!float.TryParse(arg.Args[2], out coords.y))
{
SendReply(arg, string.Format(GetMessage("invalidCoordinate", userIDString), "Y"));
return;
}
if (!float.TryParse(arg.Args[3], out coords.z))
{
SendReply(arg, string.Format(GetMessage("invalidCoordinate", userIDString), "Z"));
return;
}
if (!CheckBoundaries(coords.x, coords.y, coords.z))
{
SendReply(arg, GetMessage("coordinatesOutOfBoundaries", userIDString));
return;
}
if (arg.Args.Length > 4) if (!int.TryParse(arg.Args[4], out callNum)) callNum = 1;
newHelis = callHelis(callNum, coords, setPositionAfterSpawn: SpawnHeliOnTarget);
if (newHelis.Count > 0 && noDrop) for (int i = 0; i < newHelis.Count; i++) newHelis[i].maxCratesToSpawn = 0;
SendReply(arg, string.Format(GetMessage("helisCalledPlayer", userIDString), callNum, coords));
return;
}

ulong ID;
var target = ulong.TryParse(arg.Args[0], out ID) ? FindPlayerByID(ID) : FindPlayerByPartialName(arg.Args[0]);

if (target == null)
{
SendReply(arg, string.Format(GetMessage("playerNotFound", userIDString), arg.Args[0]));
return;
}

var num = 1;
if (arg.Args.Length == 2 && !int.TryParse(arg.Args[1], out num)) num = 1;
newHelis = callHelis(num, target?.transform?.position ?? Vector3.zero, setPositionAfterSpawn: SpawnHeliOnTarget);
if (newHelis.Count > 0 && noDrop) for (int i = 0; i < newHelis.Count; i++) newHelis[i].maxCratesToSpawn = 0;
SendReply(arg, string.Format(GetMessage("helisCalledPlayer", userIDString), num, target.displayNam

@hOst1k
Error while compiling HeliControl: ) expected | Line: 1705, Pos: 99

Did something get cut off in your paste by chance?