C#:
using ConVar;
using Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Physics = UnityEngine.Physics;
using Pool = Facepunch.Pool;
using Random = UnityEngine.Random;
using Time = UnityEngine.Time;
namespace Oxide.Plugins {
[Info("Custom Helicopter Tiers", "https://discord.gg/NUXhMfyc", "2.2.1")]
[Description("Create and customize unlimited tiers of helicopters with advanced configurations.")]
class CustomHelicopterTiers : RustPlugin {
#region References
[PluginReference]
private Plugin ServerRewards;
[PluginReference]
private Plugin Economics;
[PluginReference]
private Plugin TruePVE;
#endregion References
#region Config Auto-Update
private bool AutoUpdateConfig(DynamicConfigFile configFile) {
PrintWarning("Checking for auto-update config...");
if (configFile == null) {
PrintWarning("Invalid config file! auto-update skipped.");
return false;
}
var config = configFile["Config"] as Dictionary<string, object>;
var latestVersion = new PluginConfig().Config.Version;
if (config == null) {
PrintWarning("Config is either new or not supported.auto-update skipped.loading default config...");
ApplyDefaultConfig();
return false;
}
object configVersion = -1;
config.TryGetValue("Config Version", out configVersion);
if (configVersion == null) {
//PrintWarning("Couldn't detect config version.consider adding \"Config Version\": 0 to your config files.auto-update skipped");
//return false;
configVersion = 1;
}
var oldVersion = -1;
if (!int.TryParse(configVersion.ToString(), out oldVersion) || oldVersion == -1) {
PrintWarning("Couldn't read config version.make sure that config version has a value and is an integer.auto-update skipped");
return false;
}
/*if (latestVersion > 0 && oldVersion == 0)
{
PrintWarning($"Auto-Updating config version {oldVersion} to {latestVersion} started...");
var helicopterTiers = config["Helicopter - Tiers"] as Dictionary<string, object>;
if (helicopterTiers == null)
{
PrintWarning("Config format is not supported.auto-update skipped.");
return false;
}
config["Config Version"] = 1;
helicopterTiers["Military"] = helicopterTiers["Regular"];
helicopterTiers["Elite"] = helicopterTiers["Regular"];
config["Helicopter - Tiers"] = helicopterTiers;
var spawnLocations = config["Spawn Locations"] as Dictionary<string, object>;
if (spawnLocations != null)
{
var itemsToRemove = new List<string>();
foreach (var location in spawnLocations)
{
var locationValue = location.Value as Dictionary<string, object>;
if (locationValue != null)
{
var position = locationValue["Position"] as Dictionary<string, object>;
if (position != null)
{
var x = -1f;
float.TryParse(position["x"].ToString(), out x);
var y = -1f;
float.TryParse(position["y"].ToString(), out y);
var z = -1f;
float.TryParse(position["z"].ToString(), out z);
if (x == 0 && y == 0 && z == 0)
{
itemsToRemove.Add(location.Key);
}
}
}
}
foreach (var itemToRemove in itemsToRemove)
{
spawnLocations.Remove(itemToRemove);
}
}
configFile["Spawn Locations"] = spawnLocations;
configFile.WriteObject(new { Config = config });
configFile.Save();
PrintWarning("Auto-Update config finished.");
if (latestVersion == 1)
return true;
oldVersion = 1;
}*/
PrintWarning("No config updates found.");
return false;
}
#endregion Config Auto-Update
#region Permissions
private const string CustomHelicopterTiersAdmin = "customhelicoptertiers.admin";
public static string GetTierSpawnPermissionName(string tierName) => "customhelicoptertiers.call." + tierName.ToLower();
public static string GetLimitPermissionName(string limitName) => "customhelicoptertiers.limits." + limitName.ToLower();
public static string AllTierSpawnPermissionName = GetTierSpawnPermissionName("all");
#endregion Permissions
#region Fields and Properties
private static CustomHelicopterTiers _instance;
public const string MainFolderName = "CHT";
private PluginConfig _pluginConfig = new PluginConfig();
private const int BlueprintId = -996920608;
private static readonly string HeliPrefab = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
private static readonly float HelicopterItemsRadius = 10f;
private Dictionary<string, PlayerCommandTracker> CommandTracker = new Dictionary<string, PlayerCommandTracker>(StringComparer.InvariantCultureIgnoreCase);
private Dictionary<BaseHelicopter, int> strafeCount = new Dictionary<BaseHelicopter, int>();
private Dictionary<BaseHelicopter, HelicopterHitInfo> heliHitTracker = new Dictionary<BaseHelicopter, HelicopterHitInfo>();
private Dictionary<ulong, TempBoxData> _tempBoxes = new Dictionary<ulong, TempBoxData>();
private HashSet<HelicopterDebris> _debris = new HashSet<HelicopterDebris>();
private HashSet<FireBall> _fireBalls = new HashSet<FireBall>();
private Dictionary<string, HashSet<BaseHelicopter>> SpawnedHelicopters {
get;
set;
}
= new Dictionary<string, HashSet<BaseHelicopter>>(StringComparer.InvariantCultureIgnoreCase);
#endregion Fields & Properties
#region Hooks
void Loaded() {
if (!_pluginConfig.Config.IsEnabled) return;
LoadTrackers();
if (CommandTracker == null) CommandTracker = new Dictionary<string, PlayerCommandTracker>(StringComparer.InvariantCultureIgnoreCase);
}
void OnServerInitialized() {
if (!_pluginConfig.Config.IsEnabled) return;
SpawnedHelicopters = new Dictionary<string, HashSet<BaseHelicopter>>(StringComparer.InvariantCultureIgnoreCase);
foreach (var entity in BaseNetworkable.serverEntities) {
if (entity == null) continue;
BaseHelicopter helicopter = entity as BaseHelicopter;
HelicopterDebris debris = entity as HelicopterDebris;
FireBall fireball = entity as FireBall;
if (helicopter != null) AddToHelicopterCollection(helicopter);
if (debris != null) _debris.Add(debris);
if (fireball != null && (fireball.PrefabName.Contains("oilfireballsmall"))) _fireBalls.Add(fireball);
}
if (_pluginConfig.Config.GlobalHelicopterConfig.DisableRustDefaultHelicopter) DisableDefaultSpawning();
PatrolHelicopter.bulletAccuracy = _pluginConfig.Config.GlobalHelicopterConfig.DamageTurretsBulletAccuracy;
PatrolHelicopter.lifetimeMinutes = _pluginConfig.Config.GlobalHelicopterConfig.SpawnMaximumHelicopterLifeTime;
GetMonumentsPositions();
InitializeHelicopters();
}
private void Init() {
_instance = this;
if (!_pluginConfig.Config.IsEnabled) return;
var tiers = _pluginConfig.Config.Tiers;
if (tiers == null) {
Puts(Lang(PluginMessages.NoTiersFound));
}
else {
foreach (var tier in tiers) {
if (tier.Value == null) {
continue;
}
var spawnPermName = GetTierSpawnPermissionName(tier.Key);
if (!permission.PermissionExists(spawnPermName, this)) permission.RegisterPermission(spawnPermName, this);
if (tier.Value.CallCommand.CommandLimits != null) foreach (var limit in tier.Value.CallCommand.CommandLimits) {
var permissionName = GetLimitPermissionName(limit.Key);
if (!permission.PermissionExists(permissionName)) permission.RegisterPermission(permissionName, this);
}
}
if (!permission.PermissionExists(AllTierSpawnPermissionName)) permission.RegisterPermission(AllTierSpawnPermissionName, this);
}
if (!permission.PermissionExists(CustomHelicopterTiersAdmin)) permission.RegisterPermission(CustomHelicopterTiersAdmin, this);
if (!plugins.Exists(nameof(Economics))) PrintWarning("No Economics plugin found");
if (!plugins.Exists(nameof(ServerRewards))) PrintWarning("No ServerRewards plugin found");
var allPermissions = permission.GetPermissions();
var oldKillPermissions = allPermissions.Where(x => x.StartsWith("customhelicoptertiers.kill."));
var oldSpawnPermissions = "customhelicoptertiers.spawn";
var oldEditPermissions = "customhelicoptertiers.edit";
foreach (var basePlayer in BasePlayer.allPlayerList) {
var shouldGrantAdminPermission = false;
if (permission.UserHasPermission(basePlayer.UserIDString, oldSpawnPermissions)) {
shouldGrantAdminPermission = true;
permission.RevokeUserPermission(basePlayer.UserIDString, oldSpawnPermissions);
}
if (permission.UserHasPermission(basePlayer.UserIDString, oldEditPermissions)) {
shouldGrantAdminPermission = true;
permission.RevokeUserPermission(basePlayer.UserIDString, oldEditPermissions);
}
foreach (var oldKillPermission in oldKillPermissions) {
if (permission.UserHasPermission(basePlayer.UserIDString, oldKillPermission)) {
shouldGrantAdminPermission = true;
permission.RevokeUserPermission(basePlayer.UserIDString, oldKillPermission);
}
}
if (shouldGrantAdminPermission) {
permission.GrantUserPermission(basePlayer.UserIDString, CustomHelicopterTiersAdmin, this);
}
}
}
protected override void LoadConfig() {
base.LoadConfig();
if (!AutoUpdateConfig(Config) && _pluginConfig?.Config == null) {
ApplyDefaultConfig();
SaveConfig();
}
_pluginConfig = Config.ReadObject<PluginConfig>();
SaveConfig();
Config.Settings.Converters = new JsonConverter[] {
new StringEnumConverter()
}
;
PrintWarning("Config Loaded");
}
protected override void SaveConfig() => Config.WriteObject(_pluginConfig, true);
private void ApplyDefaultConfig() {
_pluginConfig.Config = new HelicopterTierConfig {
IsEnabled = true, UseEconomics = false, UseServerRewards = true, GlobalHelicopterConfig = new GlobalHelicopterConfig {
DamageTurretsBulletAccuracy = 2, DisableRustDefaultHelicopter = true, SpawnMaximumHelicopterLifeTime = 15
}
, SpawnLocations = new Dictionary<string, SpawnLocationData>(), MonumentSpawnLocations = new Dictionary<string, SpawnLocationData>(), Tiers = GetDefaultTiers()
}
;
}
protected override void LoadDefaultConfig() {
ApplyDefaultConfig();
PrintWarning("Loading Default Config");
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
[PluginMessages.KillSingleRewardMessage]
= "<color=#CCEE33>{0}</color> has been rewarded <color=#00FF00>{1} {2}</color>", [PluginMessages.KillDoubleRewardMessage] = "<color=#CCEE33>{0}</color> has been rewarded <color=#00FF00>{1} {2}</color> and <color=#00FF00>{3} {4}</color>", [PluginMessages.ServerRewardsName] = "RP", [PluginMessages.EconomicsName] = "Money", [PluginMessages.KillStats] = "It took {0} and total {1} damage to destroy {2} and <color=#CCEE33>{3}</color> with top rotor accuracy of <color=#00FF00>{4}%</color>!", [PluginMessages.NoTiersFound] = "No Tiers Found", [PluginMessages.TierNotFound] = "Tier Not Found", [PluginMessages.TierNameExist] = "This tier name is currently exist", [PluginMessages.PlayerNotFound] = "Player Not Found", [PluginMessages.NoCustomLootsFound] = "Unable to populate heli crate with custom loot items because no items were specified in config", [PluginMessages.NoItemDefinition] = "Failed to use custom helicopter crate loot item \"{0}\" because no such item definition exists", [PluginMessages.IsNotResearchable] = "Failed create helicopter crate loot item \"{0}\" as a blueprint because it is not researchable", [PluginMessages.ItemNotFound] = "Failed to create heli crate loot item \"{0}\" because no such item exists", [PluginMessages.HelicopterCrateItems] = "--- Helicopter Crate Items ---", [PluginMessages.NoLootPermission] = "You don't have permission to loot this container.", [PluginMessages.PluginDisabled] = "This plugin is disabled.", [PluginMessages.TierAdded] = "New tier Added.", [PluginMessages.TierRemoved] = "Tier removed.", [PluginMessages.TierNameRequired] = "You must add a tier name as an argument.", [PluginMessages.TierNoCallPermission] = "You don't have permission to call this tier.", [PluginMessages.TierNoCallPermissionAdmin] = "You don't have {0} permission to call this tier.", [PluginMessages.HelicopterTierDisabled] = "That helicopter tier is disabled.", [PluginMessages.HelicopterCalledChatMessage] = "Helicopter tier {0} called for {1}", [PluginMessages.HelicopterCalledConsoleMessage] = "{0} called helicopter tier {1} for {2}", [PluginMessages.HelicopterSpawned] = "Helicopter tier {0} spawned for {1}", [PluginMessages.InvalidArgumentForKillCommand] = "You must add a tier name, or \"all\" as an argument.", [PluginMessages.TierNoKillPermission] = "You don't have permission to destroy this tier.", [PluginMessages.TierNoKillPermissionAdmin] = "You don't have {0} permission to destroy this tier.", [PluginMessages.NoHelicoptersToDestroy] = "No helicopters to destroy.", [PluginMessages.HelicoptersDestroyedChatMessage] = "You destroyed {0} tier {1} helicopter(s).", [PluginMessages.HelicoptersDestroyedConsoleMessage] = "{0} destroyed {1} tier {2} helicopter(s).", [PluginMessages.HelicopterListTitle] = "Helicopters Tiers:", //[PluginMessages.HelicopterListItem] = "Tier: {0}, Enabled: {1}, AutoSpawn: {2}, RandomSpawn: {3}, Current Active: {4}",
[PluginMessages.HelicopterListNoData]
= "Tier: {0}, No Data To Display", [PluginMessages.Days] = "Day(s)", [PluginMessages.Hours] = "Hour(s)", [PluginMessages.Minutes] = "Minute(s)", [PluginMessages.Seconds] = "Second(s)", [PluginMessages.PatrolHelicopterInfo] = "<color=#EE0000>{0}</color> Patrol Helicopter", [PluginMessages.NoTiersFoundToSpawn] = "No auto or random spawn helicopters since there are no tiers", [PluginMessages.NoEnabledTiersFoundToSpawn] = "No auto or random spawn helicopters since all tiers are disabled", [PluginMessages.NoAutoSpawnTiersFound] = "No auto spawn helicopters found to spawn", [PluginMessages.NoRandomSpawnTiersFound] = "No random spawn helicopters found to spawn", [PluginMessages.CooldownMessage] = "You can use this again in {0}.", [PluginMessages.DailyLimitMessage] = "You have reached your daily limit for this helicopter ({0}).", [PluginMessages.DefaultHelicopterSpawningDisabled] = "Default helicopter spawning disabled", [PluginMessages.WrongCommand] = "Wrong Command, Use <color=#5af>{0}</color> to get available commands list", [PluginMessages.NoSpawnPermission] = "You don't have permission to use this command", [PluginMessages.NoEditPermission] = "You don't have permission to use this command", [PluginMessages.InvalidCoordinates] = "Invalid Coordinates", [PluginMessages.SpawnNameRequired] = "You must add a spawn name as an argument.", [PluginMessages.SpawnPositionSet] = "Spawn position set", [PluginMessages.SpawnPositionDeleted] = "Spawn position deleted", [PluginMessages.SpawnPositionNotFound] = "Spawn Position Not Found", [PluginMessages.HelicopterLimitReached] = "Unable to spawn tier {0} helicopter, because maximum limit reached.", [PluginMessages.InvalidBoxData] = "Invalid box data", [PluginMessages.LootBoxUpdated] = "Loot box table updated for helicopter tier {0}", [PluginMessages.LootBoxCooldown] = "You should wait {0} for opening this crate", [PluginMessages.ManualSpawnBroadcastMessage] = "An aggressive {0} helicopter started flying & patrolling!", [PluginMessages.CustomSpawnBroadcastMessage] = "An aggressive {0} helicopter started flying & patrolling around monuments!", [PluginMessages.RandomSpawnBroadcastMessage] = "An aggressive {0} helicopter started flying & patrolling!", [PluginMessages.Reservation] = "{0} has been reserved to {1}"
}
, this);
}
void OnEntitySpawned(BaseNetworkable entity) {
var name = entity.ShortPrefabName;
var baseHelicopter = entity as BaseHelicopter;
if (baseHelicopter != null) {
var tierComponent = baseHelicopter.GetComponent<TieredHelicopter>();
if (tierComponent == null) return;
var baseHelicopterAi = baseHelicopter.GetComponent<PatrolHelicopterAI>();
if (baseHelicopterAi == null) return;
Helicopter helicopter;
if (!_pluginConfig.Config.Tiers.TryGetValue(tierComponent.Tier, out helicopter) || helicopter == null) {
Puts(Lang(PluginMessages.TierNotFound));
return;
}
baseHelicopter.bulletDamage = helicopter.Damage.DamageTurretsBulletDamage;
baseHelicopter.bulletSpeed = helicopter.Damage.DamageTurretsBulletSpeed;
if (baseHelicopterAi.leftGun != null && baseHelicopterAi.rightGun != null) {
baseHelicopterAi.leftGun.fireRate = baseHelicopterAi.rightGun.fireRate = helicopter.Damage.DamageTurretsFireRate;
baseHelicopterAi.leftGun.burstLength = baseHelicopterAi.rightGun.burstLength = helicopter.Damage.DamageTurretsDurationOfBurst;
baseHelicopterAi.leftGun.timeBetweenBursts = baseHelicopterAi.rightGun.timeBetweenBursts = helicopter.Damage.DamageTurretsIntervalBetweenBursts;
baseHelicopterAi.leftGun.maxTargetRange = baseHelicopterAi.rightGun.maxTargetRange = helicopter.Damage.DamageTurretsBulletRange;
}
if (Math.Abs(baseHelicopter.startHealth - helicopter.Health.HealthBodyHealth) > 0.0001f) {
baseHelicopter.startHealth = helicopter.Health.HealthBodyHealth;
baseHelicopter.InitializeHealth(helicopter.Health.HealthBodyHealth, helicopter.Health.HealthBodyHealth);
}
if (helicopter.Speed.MiscHelicopterStartupLength > 0.0f && Math.Abs(helicopter.Speed.MiscHelicopterStartupSpeed - helicopter.Speed.MiscHelicopterSpeed) > 0.0001f) {
baseHelicopterAi.maxSpeed = helicopter.Speed.MiscHelicopterStartupSpeed;
baseHelicopterAi.Invoke(() => {
if (baseHelicopterAi == null || baseHelicopterAi.gameObject == null || baseHelicopter == null || baseHelicopter.IsDestroyed || baseHelicopter.gameObject == null || baseHelicopter.IsDead()) return;
baseHelicopterAi.maxSpeed = helicopter.Speed.MiscHelicopterSpeed;
}
, helicopter.Speed.MiscHelicopterStartupLength);
}
baseHelicopter.weakspots[0].health = helicopter.Health.HealthMainRotorHealth;
baseHelicopter.weakspots[1].health = helicopter.Health.HealthTailRotorHealth;
baseHelicopter.maxCratesToSpawn = helicopter.Loot.LootMaxCrates;
baseHelicopterAi.timeBetweenRockets = Mathf.Clamp(helicopter.Damage.DamageRocketsTimeBetweenEachRocket, 0.1f, 1f);
baseHelicopterAi.numRocketsLeft = Mathf.Clamp(helicopter.Damage.DamageRocketsMaxLaunchedRockets, 0, 48);
}
else if (entity is HelicopterDebris) {
var gib = entity as HelicopterDebris;
var helis = Facepunch.Pool.GetList<BaseHelicopter>();
var helicopter = GetTierSettingsFromVicinity(entity, helis, true);
if (helicopter != null) {
if (!helicopter.Loot.DropDebris || helicopter.Loot.LootGibsHp <= 0) gib.Kill();
else {
if (Math.Abs(helicopter.Loot.LootGibsHp - 500f) > 0.0001f) {
gib.InitializeHealth(helicopter.Loot.LootGibsHp, helicopter.Loot.LootGibsHp);
gib.massReductionScalar = 5 / (helicopter.Loot.LootGibsHp / 500);
gib.SendNetworkUpdate();
}
if (Math.Abs(helicopter.Loot.LootGibsCooldown - 480f) > 0.0001f) {
gib.tooHotUntil = Time.realtimeSinceStartup + helicopter.Loot.LootGibsCooldown;
gib.SendNetworkUpdate();
}
_debris.Add(gib);
}
}
Facepunch.Pool.FreeList(ref helis);
}
else if (entity is FireBall) {
var fireball = entity as FireBall;
var helis = Facepunch.Pool.GetList<BaseHelicopter>();
var helicopter = GetTierSettingsFromVicinity(entity, helis, true);
if (helicopter != null) {
if (fireball != null && (fireball.PrefabName.Contains("oilfireballsmall"))) _fireBalls.Add(fireball);
}
Facepunch.Pool.FreeList(ref helis);
}
else if (name.Contains("rocket_heli")) {
var explosion = entity as TimedExplosive;
if (explosion == null || explosion.IsDestroyed) return;
var helis = Facepunch.Pool.GetList<BaseHelicopter>();
var heliComponent = entity.GetComponent<HelicopterRocket>();
var strafeHeli = heliComponent != null ? heliComponent.Helicopter : GetHelicopterFromVicinity(entity, helis, false, p => p != null && !p.IsDestroyed && p.gameObject != null && (p?.GetComponent<PatrolHelicopterAI>()?._currentState ?? PatrolHelicopterAI.aiState.IDLE) == PatrolHelicopterAI.aiState.STRAFE);
if (strafeHeli == null) {
//explosion.Kill();
Facepunch.Pool.FreeList(ref helis);
return;
}
explosion.creatorEntity = strafeHeli;
var tierComponent = strafeHeli.GetComponent<TieredHelicopter>();
if (tierComponent == null) {
Facepunch.Pool.FreeList(ref helis);
return;
}
Helicopter helicopter;
if (!_pluginConfig.Config.Tiers.TryGetValue(tierComponent.Tier, out helicopter) || helicopter == null) {
Facepunch.Pool.FreeList(ref helis);
return;
}
var heliAI = strafeHeli.GetComponent<PatrolHelicopterAI>();
var ownerID = (entity as BaseEntity).OwnerID;
if (helicopter.Damage.DamageRocketsMaxLaunchedRockets < 1) explosion.Kill();
else {
if (helicopter.Damage.DamageRocketsMaxLaunchedRockets > 12 && ownerID == 0) {
if (strafeHeli.IsDestroyed) {
Facepunch.Pool.FreeList(ref helis);
return;
}
var curCount = 0;
if (!strafeCount.TryGetValue(strafeHeli, out curCount)) curCount = strafeCount[strafeHeli] = 1;
else curCount = strafeCount[strafeHeli] += 1;
if (curCount >= 12) {
if (heliAI == null) {
Facepunch.Pool.FreeList(ref helis);
return;
}
var actCount = 0;
Action fireAct = null;
fireAct = new Action(() => {
if (actCount >= helicopter.Damage.DamageRocketsMaxLaunchedRockets - 12) {
InvokeHandler.CancelInvoke(heliAI, fireAct);
return;
}
actCount++;
FireRocket(strafeHeli, heliAI);
}
);
InvokeHandler.InvokeRepeating(heliAI, fireAct, helicopter.Damage.DamageRocketsTimeBetweenEachRocket, helicopter.Damage.DamageRocketsTimeBetweenEachRocket);
strafeCount[strafeHeli] = 0;
}
}
else if (helicopter.Damage.DamageRocketsMaxLaunchedRockets < 12 && heliAI.ClipRocketsLeft() > helicopter.Damage.DamageRocketsMaxLaunchedRockets) {
explosion.Kill();
Facepunch.Pool.FreeList(ref helis);
return;
}
var dmgTypes = explosion != null ? explosion.damageTypes : null;
explosion.explosionRadius = helicopter.Damage.DamageRocketsExplosionRadius;
if (dmgTypes != null && dmgTypes.Count > 0) {
for (var i = 0; i < dmgTypes.Count; i++) {
var dmg = dmgTypes[i];
if (dmg.type == Rust.DamageType.Blunt) dmg.amount = helicopter.Damage.DamageRocketsBluntDamage;
if (dmg.type == Rust.DamageType.Explosion) dmg.amount = helicopter.Damage.DamageRocketsExplosionDamage;
}
}
}
Facepunch.Pool.FreeList(ref helis);
}
}
void OnEntityKill(BaseHelicopter baseHelicopter) {
if (baseHelicopter != null) {
var tierComponent = baseHelicopter.GetComponent<TieredHelicopter>();
if (tierComponent == null) return;
Helicopter helicopter;
if (!_pluginConfig.Config.Tiers.TryGetValue(tierComponent.Tier, out helicopter) || helicopter == null) return;
CalculateStats(baseHelicopter);
HashSet<BaseHelicopter> helicopters;
if (SpawnedHelicopters.TryGetValue(tierComponent.Tier, out helicopters)) {
helicopters.Remove(baseHelicopter);
}
InitializeHelicopters(tierComponent.Tier, helicopter);
var containers = baseHelicopter.GetComponents<StorageContainer>();
var heliCrates = Pool.GetList<StorageContainer>();
Vis.Entities(baseHelicopter.transform.position, HelicopterItemsRadius, heliCrates);
if (heliCrates.Count == 0) {
Pool.FreeList(ref heliCrates);
return;
}
// Handle each crate individually
foreach (var crate in heliCrates) {
if (crate.ShortPrefabName != "heli_crate") {
continue;
}
var crateInfo = crate.gameObject.AddComponent<CrateInfo>();
crateInfo.Invoker = tierComponent.Invoker;
crateInfo.AdminBypass = helicopter.Loot.LootAdminBypassCratesCooldown;
crateInfo.OnlyInvokerCanLoot = tierComponent.Invoker != null && helicopter.Loot.LootOnlyInvokerCanLoot;
crateInfo.InvokerTeamCanLoot = tierComponent.Invoker != null && helicopter.Loot.LootInvokerTeamCanLoot;
if (helicopter.Loot.LootCratesUnlockCooldown >= 0) {
crateInfo.TimeToUnlock = DateTime.UtcNow.AddMinutes(helicopter.Loot.LootCratesUnlockCooldown);
var lockedCrate = crate as LockedByEntCrate;
if (lockedCrate != null) UnlockCrate(lockedCrate);
}
//custom loot
if (!helicopter.Loot.LootEnableCustomLootTable || helicopter.Loot.LootCustomLootTable == null) continue;
var customLoot = helicopter.Loot.LootCustomLootTable;
var lootCount = UnityEngine.Random.Range(helicopter.Loot.LootMinimumAmount, helicopter.Loot.LootMaximumAmount + 1);
if (lootCount > customLoot.Count) lootCount = customLoot.Count;
var randomLoots = customLoot.OrderBy(r => Guid.NewGuid()).Take(lootCount);
crate.inventory.Clear();
var lootCrate = crate as LootContainer;
if (lootCrate == null) {
continue;
}
lootCrate.inventory.Clear();
var items = lootCrate.inventory.itemList.ToList();
for (var i = 0; i < items.Count; i++) {
items[i].DoRemove();
}
foreach (var itemInfo in randomLoots) {
Item item;
if (Random.Range(1, 100) <= itemInfo.Rarity) {
var amount = Random.Range(itemInfo.MinAmount, itemInfo.MaxAmount + 1);
if (itemInfo.BluePrint) item = ItemManager.CreateByItemID(BlueprintId, amount, itemInfo.SkinId);
else item = ItemManager.CreateByItemID(itemInfo.ItemId, amount, itemInfo.SkinId);
if (item == null) {
Puts(Lang(PluginMessages.ItemNotFound, null, itemInfo.ItemShortName));
continue;
}
if (itemInfo.BluePrint) item.blueprintTarget = itemInfo.ItemId;
item.MoveToContainer(crate.inventory);
}
}
}
Pool.FreeList(ref heliCrates);
}
}
private void OnEntityKill(HelicopterDebris debris) {
if (debris != null) _debris.Remove(debris);
}
private void OnEntityKill(FireBall fireball) {
if (fireball != null && (fireball.PrefabName.Contains("oilfireballsmall"))) _fireBalls.Remove(fireball);
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
// Skip if no entity exists or the hit is invalid.if (entity == null || hitInfo == null)
return null;
BaseEntity attacker = hitInfo.Initiator;
// Skip if the attacker is undefined or the damage receiver is a player.if (attacker == null || entity is BasePlayer)
return null;
TieredHelicopter tieredHelicopter = attacker.GetComponent<TieredHelicopter>();
// Proceed if the attacker is a tiered helicopter.if (tieredHelicopter != null)
{
// Proceed if Player Versus Environment is enabled and the helicopter invoker exists.if (tieredHelicopter.EnablePlayerVersusEnvironment && tieredHelicopter.Invoker != null)
{
// Skip if the entity owner is the same as the helicopter invoker.if (entity.OwnerID == tieredHelicopter.Invoker.userID)
return null;
// Skip if the helicopter invoker has a team and the entity owner is one of its team members.if (tieredHelicopter.Invoker.Team != null && tieredHelicopter.Invoker.Team.members.Contains(entity.OwnerID))
return null;
// Otherwise, cancel damahe to the receiving entity.hitInfo.damageTypes.Clear();
return true;
}
// Allow damage.return null;
}
// Allow damage.return null;
}
object OnEntityTakeDamage(BaseHelicopter helicopter, HitInfo hitInfo) {
var hitter = hitInfo?.Initiator;
if (hitter == null) return null;
var tierComponent = helicopter.GetComponent<TieredHelicopter>();
if (tierComponent != null) {
if (hitter is BasePlayer) {
var playerInitiator = hitter.ToPlayer();
if (tierComponent.Invoker != null && tierComponent.LockToInvoker) {
if (tierComponent.Invoker.userID == playerInitiator.userID) {
RegisterHeliHitInfo(helicopter, playerInitiator, hitInfo);
}
else if (tierComponent.IncludeTeammates && tierComponent.Invoker.currentTeam > 0 && tierComponent.Invoker.currentTeam == playerInitiator.currentTeam) {
RegisterHeliHitInfo(helicopter, playerInitiator, hitInfo);
}
else {
hitInfo.damageTypes.Clear();
return true;
}
}
else if (tierComponent.Invoker == null) {
Helicopter tierhelicopter;
if (_pluginConfig.Config.Tiers.TryGetValue(tierComponent.Tier, out tierhelicopter) && tierhelicopter != null && tierhelicopter.PlayerVsPlayer.LockToAttacker) {
tierComponent.Invoker = playerInitiator;
Server.Broadcast(Lang(PluginMessages.Reservation, null, tierComponent.Tier, playerInitiator.displayName));
}
RegisterHeliHitInfo(helicopter, playerInitiator, hitInfo);
}
}
}
return null;
}
private object OnHelicopterStrafeEnter(PatrolHelicopterAI helicopter, Vector3 strafePosition) {
var tierComponent = helicopter.GetComponent<TieredHelicopter>();
if (tierComponent != null && tierComponent.Invoker != null && tierComponent.LockToInvoker) {
if (Vector3.Distance(strafePosition, tierComponent.Invoker.ServerPosition) < 50) {
return null;
}
if (tierComponent.IncludeTeammates && tierComponent.Invoker.currentTeam > 0) {
foreach (var teamMemberId in tierComponent.Invoker.Team.members) {
var teamMember = BasePlayer.FindByID(teamMemberId);
if (teamMember != null && Vector3.Distance(strafePosition, teamMember.ServerPosition) < 50) {
return null;
}
}
}
return false;
}
return null;
}
private object CanHelicopterTarget(PatrolHelicopterAI helicopter, BasePlayer player) {
var tierComponent = helicopter.GetComponent<TieredHelicopter>();
if (tierComponent != null && tierComponent.Invoker != null && tierComponent.LockToInvoker) {
if (tierComponent.Invoker.userID == player.userID) {
return null;
}
if (tierComponent.IncludeTeammates && tierComponent.Invoker.currentTeam > 0 && tierComponent.Invoker.currentTeam == player.currentTeam) {
return null;
}
return false;
}
return null;
}
private object CanHelicopterStrafeTarget(PatrolHelicopterAI heli, BasePlayer target) => CanHelicopterTarget(heli, target);
private object OnHelicopterTarget(HelicopterTurret turret, BasePlayer player) {
var tierComponent = turret._heliAI.GetComponent<TieredHelicopter>();
if (tierComponent != null && tierComponent.Invoker != null && tierComponent.LockToInvoker) {
if (tierComponent.Invoker.userID == player.userID) {
return null;
}
if (tierComponent.IncludeTeammates && tierComponent.Invoker.currentTeam > 0 && tierComponent.Invoker.currentTeam == player.currentTeam) {
return null;
}
return false;
}
return null;
}
object CanLootEntity(BasePlayer player, StorageContainer container) {
if (container.ShortPrefabName != "heli_crate") return null;
var crateInfo = container.GetComponent<CrateInfo>();
if (crateInfo == null) return null;
if (crateInfo.AdminBypass && player.IsAdmin) return null;
if (crateInfo.OnlyInvokerCanLoot && crateInfo.Invoker != player) {
if (!crateInfo.InvokerTeamCanLoot || crateInfo.Invoker.currentTeam == 0 || crateInfo.Invoker.currentTeam != player.currentTeam) {
SendReply(player, Lang(PluginMessages.NoLootPermission, player.UserIDString));
return false;
}
}
if (!crateInfo.TimeToUnlock.HasValue) {
return null;
}
if (!WaterLevel.Test(container.ServerPosition) && crateInfo.TimeToUnlock.Value > DateTime.UtcNow) {
player.ChatMessage(Lang(PluginMessages.LootBoxCooldown, player.UserIDString, Humanize(crateInfo.TimeToUnlock.Value.Subtract(DateTime.UtcNow))));
return false;
}
return null;
}
private void OnPlayerLootEnd(PlayerLoot inventory) {
var player = inventory.GetComponent<BasePlayer>();
TempBoxData boxData;
if (inventory.entitySource != null && _tempBoxes.TryGetValue(player.userID, out boxData) && boxData != null && inventory.entitySource is StorageContainer) {
StoreTempBoxData(player, inventory.entitySource as StorageContainer, boxData.Tier);
}
}
object OnHelicopterRetire(PatrolHelicopterAI entity) {
if (heliHitTracker != null && heliHitTracker.ContainsKey(entity.helicopterBase)) {
heliHitTracker.Remove(entity.helicopterBase);
}
return null;
}
private void Unload() {
_instance = null;
}
#endregion Hooks
#region Commands
// heli.help
[ChatCommand(Commands.Help)]
void HelicopterHelpChatCommand(BasePlayer player, string cmd, string[] args) {
SendReply(player, GetCommands(player));
}
// heli.call <Tier>
// heli.call <Tier> here
[ChatCommand(Commands.Call)]
void CallHelicopterTier(BasePlayer player, string cmd, string[] args) {
if (!_pluginConfig.Config.IsEnabled) {
SendReply(player, Lang(PluginMessages.PluginDisabled, player.UserIDString));
return;
}
if (args.Length == 0) {
SendReply(player, Lang(PluginMessages.TierNameRequired, player.UserIDString));
return;
}
var tierSelected = args[0];
if (!_pluginConfig.Config.Tiers.ContainsKey(tierSelected)) {
SendReply(player, Lang(PluginMessages.TierNotFound, player.UserIDString));
return;
}
var userPermissions = permission.GetUserPermissions(player.UserIDString);
var permissionName = GetTierSpawnPermissionName(tierSelected);
if (!userPermissions.Contains(permissionName) && !userPermissions.Contains(AllTierSpawnPermissionName) && !userPermissions.Contains(CustomHelicopterTiersAdmin)) {
SendReply(player, player.IsAdmin ? Lang(PluginMessages.TierNoCallPermissionAdmin, player.UserIDString, permissionName) : Lang(PluginMessages.TierNoCallPermission, player.UserIDString));
return;
}
var helicopter = _pluginConfig.Config.Tiers[tierSelected];
if (!helicopter.Enabled) {
SendReply(player, Lang(PluginMessages.HelicopterTierDisabled, player.UserIDString));
return;
}
if (!PlayerCanSpawnTier(player, tierSelected, helicopter)) return;
var location = Vector3.zero;
if (args.Length > 1 && args[1] == "here") {
location = player.transform.position;
}
else {
if (helicopter.Spawn.SpawnCustomEnabled) {
List<string> validSpawns = GetValidSpawnPointsIncludingMonuments(helicopter);
var spawnLocationName = validSpawns?.GetRandom();
if (!string.IsNullOrWhiteSpace(spawnLocationName)) {
location = GetSpawnPointLocationIncludingMonuments(spawnLocationName);
}
}
}
CallHelicopterToPlayer(tierSelected, location, player, player);
}
// heli.kill <Tier>
// heli.kill all
[ChatCommand(Commands.Kill)]
void KillHeli(BasePlayer player, string cmd, string[] args) {
if (!_pluginConfig.Config.IsEnabled) {
SendReply(player, Lang(PluginMessages.PluginDisabled, player.UserIDString));
return;
}
if (args.Length == 0) {
SendReply(player, Lang(PluginMessages.InvalidArgumentForKillCommand, player.UserIDString));
return;
}
var tierSelected = args[0].ToLower();
if (tierSelected != "all" && !_pluginConfig.Config.Tiers.ContainsKey(tierSelected)) {
SendReply(player, Lang(PluginMessages.TierNotFound, player.UserIDString));
return;
}
var userHasAllAdminPermission = permission.UserHasPermission(player.UserIDString, CustomHelicopterTiersAdmin);
if (!userHasAllAdminPermission) {
SendReply(player, player.IsAdmin ? Lang(PluginMessages.TierNoKillPermissionAdmin, player.UserIDString, CustomHelicopterTiersAdmin) : Lang(PluginMessages.TierNoKillPermission, player.UserIDString));
return;
}
if (SpawnedHelicopters.Count == 0) {
SendReply(player, Lang(PluginMessages.NoHelicoptersToDestroy, player.UserIDString));
return;
}
foreach (var spawnedHelicopter in SpawnedHelicopters) {
if (tierSelected != "all" && !tierSelected.Equals(spawnedHelicopter.Key.ToLower())) {
continue;
}
var destroyedCount = DestroySpawnedTier(spawnedHelicopter.Key, spawnedHelicopter.Value.ToList());
SendReply(player, Lang(PluginMessages.HelicoptersDestroyedChatMessage, player.UserIDString, destroyedCount, spawnedHelicopter.Key));
Puts(Lang(PluginMessages.HelicoptersDestroyedConsoleMessage, player.UserIDString, player.displayName, destroyedCount, spawnedHelicopter.Key));
}
}
// heli.spawn set <SpawnPointName> <here|Coordinates>
// heli.spawn delete <SpawnPointName>
// heli.spawn show
[ChatCommand(Commands.Spawn)]
void SpawnHelicopterChatCommand(BasePlayer player, string cmd, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, CustomHelicopterTiersAdmin)) {
SendReply(player, Lang(PluginMessages.NoSpawnPermission, player.UserIDString));
return;
}
if (!_pluginConfig.Config.IsEnabled) {
SendReply(player, Lang(PluginMessages.PluginDisabled, player.UserIDString));
return;
}
if (args.Length == 0) {
SendReply(player, Lang(PluginMessages.WrongCommand, player.UserIDString, Commands.Help));
return;
}
switch (args[0]) {
case "set" : {
if (args.Length < 2) {
SendReply(player, Lang(PluginMessages.SpawnNameRequired, player.UserIDString));
return;
}
var spawnLocation = player.ServerPosition;
if (args.Length > 2) {
if (args[2].Equals("here", StringComparison.OrdinalIgnoreCase)) {
spawnLocation = player.ServerPosition;
}
else {
var xString = "";
var yString = "";
var zString = "";
if (args.Length == 3) {
var coordinates = args[2].Split(',');
if (coordinates.Length != 3) {
SendReply(player, Lang(PluginMessages.InvalidCoordinates, player.UserIDString));
return;
}
xString = coordinates[0];
yString = coordinates[1];
zString = coordinates[2];
}
else if (args.Length != 5) {
SendReply(player, Lang(PluginMessages.InvalidCoordinates, player.UserIDString));
return;
}
else {
xString = args[2];
yString = args[3];
zString = args[4];
}
float x, y, z;
if (!float.TryParse(xString, out x)) {
SendRepl
// ... [Code preview truncated due to length. Full source in attached file] ...