Иконка ресурса

FREE HelpfulSupply : 1.0.9 1.0.9

Нет прав для скачивания
C#:
//#define TESTING

#if TESTING
using System.Diagnostics;
#endif

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Oxide.Plugins
{
	[Info("Helpful Supply", "Mevent", "1.0.9")]
	public class HelpfulSupply : RustPlugin
	{
		#region Fields

		[PluginReference] private Plugin
			Loottable = null,
			Clans = null,
			Friends = null;

		private static HelpfulSupply _instance;

		private readonly Dictionary<ulong, Configuration.SupplyInfo> _supplyToInfo =
			new Dictionary<ulong, Configuration.SupplyInfo>();

		private readonly Dictionary<ulong, Configuration.SupplyInfo> _cargoToInfo =
			new Dictionary<ulong, Configuration.SupplyInfo>();

		private readonly Dictionary<ulong, Configuration.SupplyInfo> _supplyBySkinId =
			new Dictionary<ulong, Configuration.SupplyInfo>();

		private const int CAST_LAYERS = (1 << 0) | (1 << 4) | (1 << 8) | (1 << 10) | (1 << 16) | (1 << 21) | (1 << 23) |
		                                (1 << 25) | (1 << 26);

		private const string PARACHUTE_PREFAB = "assets/prefabs/misc/parachute/parachute.prefab";

		private const string DROP_PREFAB = "assets/prefabs/misc/supply drop/supply_drop.prefab";

		private const ulong DROP_OWNERID = 1034003435UL;

		private readonly Dictionary<string, List<Configuration.SupplyInfo>> _containerToSupplies =
			new Dictionary<string, List<Configuration.SupplyInfo>>();

		private readonly Dictionary<ulong, Configuration.SupplyInfo> _ownerToInfo =
			new Dictionary<ulong, Configuration.SupplyInfo>();

		private bool _hasLoottables;

		#endregion

		#region Config

		private Configuration _config;

		private class Configuration
		{
			[JsonProperty(PropertyName = "Supplies", ObjectCreationHandling = ObjectCreationHandling.Replace)]
			public List<SupplyInfo> Supplies = new List<SupplyInfo>
			{
				new SupplyInfo
				{
					Name = "heal",
					Item = new ItemInfo("Medicine Supply Signal", "supply.signal", 2912968568),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 12),
						Items = new List<LootItem>
						{
							new LootItem(string.Empty, "antiradpills", 0, new AmountInfo(2, 4), 70),
							new LootItem(string.Empty, "bandage", 0, new AmountInfo(5, 10), 50),
							new LootItem(string.Empty, "largemedkit", 0, new AmountInfo(1, 2), 60),
							new LootItem(string.Empty, "syringe.medical", 0, new AmountInfo(1, 2), 60),
							new LootItem(string.Empty, "bandage", 0, new AmountInfo(10, 15), 20),
							new LootItem(string.Empty, "largemedkit", 0, new AmountInfo(10, 12), 5),
							new LootItem(string.Empty, "syringe.medical", 0, new AmountInfo(15, 25), 10)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "guns",
					Item = new ItemInfo("Guns Supply Signal", "supply.signal", 2912968440),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 12),
						Items = new List<LootItem>
						{
							new LootItem(string.Empty, "rifle.ak", 0, new AmountInfo(1, 1), 15),
							new LootItem(string.Empty, "rifle.lr300", 0, new AmountInfo(1, 1), 15),
							new LootItem(string.Empty, "rifle.l96", 0, new AmountInfo(1, 1), 5),
							new LootItem(string.Empty, "rifle.bolt", 0, new AmountInfo(1, 1), 10),
							new LootItem(string.Empty, "rifle.m39", 0, new AmountInfo(1, 1), 15),
							new LootItem(string.Empty, "rifle.semiauto", 0, new AmountInfo(1, 1), 25),
							new LootItem(string.Empty, "pistol.m92", 0, new AmountInfo(1, 1), 20),
							new LootItem(string.Empty, "pistol.python", 0, new AmountInfo(1, 1), 25),
							new LootItem(string.Empty, "pistol.revolver", 0, new AmountInfo(1, 1), 25),
							new LootItem(string.Empty, "pistol.semiauto", 0, new AmountInfo(1, 1), 20)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "explosive",
					Item = new ItemInfo("Explosive Supply Signal", "supply.signal", 2912968298),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 6),
						Items = new List<LootItem>
						{
							new LootItem(string.Empty, "explosive.satchel", 0, new AmountInfo(1, 2), 70),
							new LootItem(string.Empty, "explosive.satchel", 0, new AmountInfo(8, 12), 55),
							new LootItem(string.Empty, "explosive.satchel", 0, new AmountInfo(20, 20), 15),
							new LootItem(string.Empty, "explosive.timed", 0, new AmountInfo(1, 1), 50),
							new LootItem(string.Empty, "explosive.timed", 0, new AmountInfo(4, 8), 15),
							new LootItem(string.Empty, "explosive.timed", 0, new AmountInfo(4, 8), 5),
							new LootItem(string.Empty, "explosives", 0, new AmountInfo(10, 20), 60),
							new LootItem(string.Empty, "explosives", 0, new AmountInfo(20, 30), 10),
							new LootItem(string.Empty, "ammo.rifle.explosive", 0, new AmountInfo(64, 128), 75),
							new LootItem(string.Empty, "ammo.rifle.explosive", 0, new AmountInfo(128, 256), 40),
							new LootItem(string.Empty, "ammo.rifle.explosive", 0, new AmountInfo(256, 512), 10),
							new LootItem(string.Empty, "ammo.rocket.basic", 0, new AmountInfo(1, 4), 40),
							new LootItem(string.Empty, "ammo.rocket.basic", 0, new AmountInfo(10, 20), 10),
							new LootItem(string.Empty, "rocket.launcher", 0, new AmountInfo(1, 1), 45),
							new LootItem(string.Empty, "ammo.grenadelauncher.he", 0, new AmountInfo(4, 12), 55),
							new LootItem(string.Empty, "grenade.beancan", 0, new AmountInfo(4, 16), 60),
							new LootItem(string.Empty, "grenade.beancan", 0, new AmountInfo(16, 24), 25)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "clothes",
					Item = new ItemInfo("Clothes Supply Signal", "supply.signal", 2912968179),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 12),
						Items = new List<LootItem>
						{
							new LootItem(string.Empty, "coffeecan.helmet", 0, new AmountInfo(1, 2), 70),
							new LootItem(string.Empty, "hazmatsuit", 0, new AmountInfo(1, 1), 60),
							new LootItem(string.Empty, "hoodie", 0, new AmountInfo(1, 1), 80),
							new LootItem(string.Empty, "pants", 0, new AmountInfo(1, 1), 80),
							new LootItem(string.Empty, "shoes.boots", 0, new AmountInfo(1, 1), 80),
							new LootItem(string.Empty, "roadsign.jacket", 0, new AmountInfo(1, 1), 50),
							new LootItem(string.Empty, "roadsign.kilt", 0, new AmountInfo(1, 1), 50),
							new LootItem(string.Empty, "metal.facemask", 0, new AmountInfo(1, 1), 25),
							new LootItem(string.Empty, "metal.plate.torso", 0, new AmountInfo(1, 1), 25),
							new LootItem(string.Empty, "heavy.plate.jacket", 0, new AmountInfo(1, 1), 15),
							new LootItem(string.Empty, "heavy.plate.pants", 0, new AmountInfo(1, 1), 15)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "cash",
					Item = new ItemInfo("Cash Supply Signal", "supply.signal", 2912967918),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 4),
						Items = new List<LootItem>
						{
							new LootItem("RUSTNote", "sticks", 2536195910, new AmountInfo(5, 10), 75),
							new LootItem("RUSTNote", "sticks", 2536195910, new AmountInfo(10, 20), 70),
							new LootItem("RUSTNote", "sticks", 2536195910, new AmountInfo(20, 50), 40),
							new LootItem("RUSTNote", "sticks", 2536195910, new AmountInfo(50, 150), 20),
							new LootItem("RUSTNote", "sticks", 2536195910, new AmountInfo(150, 300), 5)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "cars",
					Item = new ItemInfo("Cars Supply Signal", "supply.signal", 2912968057),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(1, 2),
						Items = new List<LootItem>
						{
							new LootItem("RUSTSharing Ferrari", "box.wooden.large", 2591806406, new AmountInfo(1, 1),
								40),
							new LootItem("RUSTSharing Mazda", "box.wooden.large", 2591806406, new AmountInfo(1, 1),
								40),
							new LootItem("RUSTSharing Skoda", "box.wooden.large", 2591806406, new AmountInfo(1, 1),
								40),
							new LootItem("RUSTSharing Opel", "box.wooden.large", 2591806406, new AmountInfo(1, 1), 40)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				},
				new SupplyInfo
				{
					Name = "lucky",
					Item = new ItemInfo("Lucky Supply Signal", "supply.signal", 2912968671),
					Loot = new LootInfo
					{
						Access = new AccessInfo
						{
							AllPlayers = true,
							Teammates = true,
							Friends = true,
							Clanmates = true
						},
						Speed = new DropSpeed
						{
							Enabled = false,
							DropVelocity = 120f,
							DropGroundDistance = 120f
						},
						Amount = new AmountInfo(2, 12),
						Items = new List<LootItem>
						{
							new LootItem(string.Empty, "sulfur", 0, new AmountInfo(1000, 3000), 60),
							new LootItem(string.Empty, "sulfur", 0, new AmountInfo(3000, 9000), 20),
							new LootItem(string.Empty, "metal.refined", 0, new AmountInfo(50, 100), 60),
							new LootItem(string.Empty, "metal.refined", 0, new AmountInfo(100, 250), 15),
							new LootItem(string.Empty, "gunpowder", 0, new AmountInfo(1000, 3000), 55),
							new LootItem(string.Empty, "gunpowder", 0, new AmountInfo(3000, 9000), 15),
							new LootItem(string.Empty, "metal.fragments", 0, new AmountInfo(3000, 9000), 50),
							new LootItem(string.Empty, "metal.fragments", 0, new AmountInfo(9000, 15000), 30),
							new LootItem(string.Empty, "scrap", 0, new AmountInfo(150, 300), 50),
							new LootItem(string.Empty, "scrap", 0, new AmountInfo(300, 900), 25),
							new LootItem(string.Empty, "autoturret", 0, new AmountInfo(1, 1), 45)
						}
					},
					Drop = new DropInfo
					{
						Enabled = false,
						Containers = new List<DropContainer>
						{
							new DropContainer("assets/bundled/prefabs/radtown/crate_normal.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
								5f,
								new AmountInfo(1, 1)),
							new DropContainer("assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
								5f,
								new AmountInfo(1, 1))
						}
					}
				}
			};

			public VersionNumber Version;

			#region Classes

			public class DropInfo
			{
				[JsonProperty(PropertyName = "Enabled")]
				public bool Enabled;

				[JsonProperty(PropertyName = "Containers", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public List<DropContainer> Containers = new List<DropContainer>();
			}

			public class DropContainer
			{
				[JsonProperty(PropertyName = "Prefab")]
				public string PrefabName;

				[JsonProperty(PropertyName = "Chance")]
				public float Chance;

				[JsonProperty(PropertyName = "Amount")]
				public AmountInfo Amount;

				public DropContainer(string prefabName, float chance, AmountInfo amount)
				{
					PrefabName = prefabName;
					Chance = chance;
					Amount = amount;
				}
			}

			public class SupplyInfo
			{
				[JsonProperty(PropertyName = "Name")] public string Name;

				[JsonProperty(PropertyName = "Item Settings")]
				public ItemInfo Item;

				[JsonProperty(PropertyName = "Loot Settings")]
				public LootInfo Loot;

				[JsonProperty(PropertyName = "Drop Settings")]
				public DropInfo Drop;

				[JsonIgnore] private ulong _ownerId;

				[JsonIgnore]
				public ulong OwnerID
				{
					get
					{
						if (_ownerId == 0)
							GenerateOwnerId();

						return _ownerId;
					}
				}

				public void GenerateOwnerId()
				{
					do
					{
						var val = (ulong) Random.Range(0, ulong.MaxValue);

						if (_instance._ownerToInfo.ContainsKey(val) || val == _ownerId) continue;

						_ownerId = val;
						_instance._ownerToInfo[val] = this;
					} while (_ownerId == 0);
				}
			}

			public class ItemInfo
			{
				[JsonProperty(PropertyName = "Display Name")]
				public string DisplayName;

				[JsonProperty(PropertyName = "ShortName")]
				public string ShortName;

				[JsonProperty(PropertyName = "Skin")] public ulong Skin;

				public Item ToItem(int amount = 1)
				{
					var item = ItemManager.CreateByName(ShortName, Mathf.Max(amount, 1), Skin);
					if (item == null)
					{
						Debug.LogError($"Error creating item with ShortName: '{ShortName}'");
						return null;
					}

					if (!string.IsNullOrEmpty(DisplayName))
						item.name = DisplayName;

					return item;
				}

				public ItemInfo(string displayName, string shortName, ulong skin)
				{
					DisplayName = displayName;
					ShortName = shortName;
					Skin = skin;
				}
			}

			public class LootItem : ItemInfo
			{
				[JsonProperty(PropertyName = "Amount")]
				public AmountInfo Amount;

				[JsonProperty(PropertyName = "Chance")]
				public float Chance;

				public Item ToItem()
				{
					return ToItem(Amount.Get());
				}

				public LootItem(string displayName, string shortName, ulong skin, AmountInfo amount, float chance) :
					base(displayName, shortName, skin)
				{
					Amount = amount;
					Chance = chance;
				}
			}

			public class AccessInfo
			{
				[JsonProperty(PropertyName = "All players")]
				public bool AllPlayers = true;

				[JsonProperty(PropertyName = "Teammates (Rust in-game system)")]
				public bool Teammates = true;

				[JsonProperty(PropertyName = "Friends")]
				public bool Friends = true;

				[JsonProperty(PropertyName = "Clanmates")]
				public bool Clanmates = true;
			}

			public class LootInfo
			{
				[JsonProperty(PropertyName = "Access to loot")]
				public AccessInfo Access = new AccessInfo();

				[JsonProperty(PropertyName = "Speed")] public DropSpeed Speed;

				[JsonProperty(PropertyName = "Amount")]
				public AmountInfo Amount;

				[JsonProperty(PropertyName = "Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public List<LootItem> Items = new List<LootItem>();

				private List<LootItem> GetRandomItems()
				{
					var amount = Amount.Get();

					var list = new List<LootItem>();

					for (var i = 0; i < amount; i++)
					{
						LootItem item = null;

						var iteration = 0;
						do
						{
							iteration++;

							var randomItem = Items.GetRandom();
							if (randomItem.Chance <= 0 || randomItem.Chance > 100) continue;

							if (Random.Range(0f, 100f) <= randomItem.Chance) item = randomItem;
						} while (item == null && iteration < 1000);

						if (item != null)
							list.Add(item);
					}

					return list;
				}

				public void DoPopulate(LootContainer container, ulong owner)
				{
					if (container.inventory == null)
						return;

#if TESTING
					_instance?.PrintInventoryContents(container.inventory,
						"DoPopulate, container {container.name} inventory before clear");
#endif

					ClearContainer(container.inventory);

#if TESTING
					_instance?.PrintInventoryContents(container.inventory,
						"DoPopulate, container {container.name} inventory after clear");
#endif
					FillContainer(container);

#if TESTING
					_instance?.PrintInventoryContents(container.inventory,
						"DoPopulate, container {container.name} inventory after fill");
#endif

					container.inventory.playerOwner = BasePlayer.FindByID(owner);
				}

				private void FillContainer(LootContainer container)
				{
					var items = GetRandomItems();
					if (items.Count > 0)
					{
						container.inventory = new ItemContainer
						{
							entityOwner = container
						};
						container.inventory.ServerInitialize(null, items.Count);
						container.inventory.GiveUID();

						items.ForEach(item => item?.ToItem()?.MoveToContainer(container.inventory, -1, false));
					}
				}

				private void ClearContainer(ItemContainer itemContainer)
				{
					if (itemContainer?.itemList == null)
						return;

					while (itemContainer.itemList.Count > 0)
					{
						var item = itemContainer.itemList[0];
						item.RemoveFromContainer();
						item.Remove();
						item.DoRemove();
					}
				}
			}

			public class AmountInfo
			{
				[JsonProperty(PropertyName = "Minimal amount")]
				public int Min;

				[JsonProperty(PropertyName = "Maximum amount")]
				public int Max;

				public AmountInfo(int min, int max)
				{
					Min = min;
					Max = max;
				}

				public int Get()
				{
					return Random.Range(Min, Max);
				}
			}

			public class DropSpeed
			{
				[JsonProperty(PropertyName = "Enabled?")]
				public bool Enabled;

				[JsonProperty(PropertyName = "Incoming Space Delivery Supply Drop velocity")]
				public float DropVelocity = 120f;

				[JsonProperty(PropertyName = "Parachute deploy distance from ground")]
				public float DropGroundDistance = 120f;
			}

			#endregion
		}

		protected override void LoadConfig()
		{
			base.LoadConfig();
			try
			{
				_config = Config.ReadObject<Configuration>();
				if (_config == null) throw new Exception();

				if (_config.Version < Version)
					UpdateConfigValues();
			}
			catch (Exception ex)
			{
				PrintError("Your configuration file contains an error. Using default configuration values.");
				Debug.LogError(ex.Message);
				LoadDefaultConfig();
			}
		}

		protected override void SaveConfig()
		{
			Config.WriteObject(_config);
		}

		protected override void LoadDefaultConfig()
		{
			_config = new Configuration();
		}

		private void UpdateConfigValues()
		{
			PrintWarning("Config update detected! Updating config values...");

			if (_config.Version < new VersionNumber(1, 0, 5))
				_config.Supplies.ForEach(supply => supply.Loot.Access = new Configuration.AccessInfo());

			_config.Version = Version;

			SaveConfig();

			PrintWarning("Config update completed!");
		}

		#endregion

		#region Hooks

		private void OnServerInitialized()
		{
			_instance = this;

			TryCacheSupplies();

			UseLottable();

#if TESTING
			StopwatchWrapper.OnComplete = DebugMessage;
#endif
		}

		private void Unload()
		{
			_instance = null;
		}

		private void OnExplosiveDropped(BasePlayer player, SupplySignal supplySignal, ThrownWeapon weapon)
		{
#if TESTING
			Puts($"OnExplosiveThrown: player={player}, supplySignal={supplySignal}, weapon={weapon}");
#endif
			OnExplosiveThrown(player, supplySignal, weapon);
		}

		private void OnExplosiveThrown(BasePlayer player, SupplySignal supplySignal, ThrownWeapon weapon)
		{
			if (player == null ||
			    weapon == null ||
			    weapon.skinID == 0 ||
			    supplySignal == null ||
			    supplySignal.net == null)
			{
#if TESTING
				Puts(
					$"OnExplosiveThrown returned: player={player?.ToString() ?? "null"}, supplySignal={supplySignal?.ToString() ?? "null"}, weapon={weapon?.ToString() ?? "null"}");
#endif
				return;
			}

			var info = FindSupplyInfo(weapon.skinID);
			if (info == null)
			{
#if TESTING
				Puts($"OnExplosiveThrown: info for skinID {weapon.skinID} not found");
#endif
				return;
			}

			supplySignal.OwnerID = player.userID;

			_supplyToInfo[supplySignal.net.ID.Value] = info;

#if TESTING
			Puts(
				$"OnExplosiveThrown: supplySignal.OwnerID={supplySignal.OwnerID}, supplySignal.net.ID.Value={supplySignal.net.ID.Value}, _supplyToInfo={_supplyToInfo.ContainsKey(supplySignal.net.ID.Value)}, info.OwnerId={info.OwnerID}");
#endif
		}

		private void OnCargoPlaneSignaled(CargoPlane cargoPlane, SupplySignal supplySignal)
		{
			if (cargoPlane == null || supplySignal == null ||
			    supplySignal.net == null)
			{
#if TESTING
				Puts(
					$"OnCargoPlaneSignaled returned: cargoPlane={cargoPlane?.ToString() ?? "null"}, supplySignal={supplySignal?.ToString() ?? "null"}");
#endif
				return;
			}

			Configuration.SupplyInfo info;
			if (!_supplyToInfo.TryGetValue(supplySignal.net.ID.Value, out info))
			{
#if TESTING
				Puts($"OnCargoPlaneSignaled: info for supplySignal.net.ID={supplySignal.net.ID.Value} not found");
#endif
				return;
			}

			NextTick(() =>
			{
				if (cargoPlane != null && cargoPlane.net != null)
					_cargoToInfo[cargoPlane.net.ID.Value] = info;

				cargoPlane.OwnerID = supplySignal.OwnerID;

				_supplyToInfo.Remove(supplySignal.net.ID.Value);

#if TESTING
				Puts(
					$"OnCargoPlaneSignaled: cargoPlane.OwnerID={cargoPlane.OwnerID}, cargoPlane.net.ID.Value={cargoPlane.net?.ID.Value}, _cargoToInfo={_cargoToInfo.ContainsKey(cargoPlane.net?.ID.Value ?? 0)}, info.OwnerId={info.OwnerID}");
#endif
			});
		}

		private void OnSupplyDropDropped(SupplyDrop supplyDrop, CargoPlane cargoPlane)
		{
			if (supplyDrop == null || cargoPlane == null || cargoPlane.net == null)
			{
#if TESTING
				Puts(
					$"OnSupplyDropDropped returned: supplyDrop={supplyDrop?.ToString() ?? "null"}, cargoPlane={cargoPlane?.ToString() ?? "null"}");
#endif
				return;
			}

			Configuration.SupplyInfo info;
			if (!_cargoToInfo.TryGetValue(cargoPlane.net.ID.Value, out info))
			{
#if TESTING
				Puts($"OnSupplyDropDropped: info for cargoPlane.net.ID={cargoPlane.net.ID.Value} not found");
#endif
				return;
			}

			supplyDrop.Kill();

			var transform = cargoPlane.transform;

			var dropPos = transform.position;
			dropPos.y -= 2f;

#if TESTING
			Puts(
				$"Calling SpawnSupplyDrop method with info={info.OwnerID}, dropPos={dropPos}, transform.rotation={transform.rotation}, cargoPlane.OwnerID={cargoPlane.OwnerID}, info.OwnerId={info.OwnerID}");
#endif
			SpawnSupplyDrop(info, dropPos, transform.rotation, cargoPlane.OwnerID);

			_cargoToInfo.Remove(cargoPlane.net.ID.Value);

#if TESTING
			Puts($"OnSupplyDropDropped: cargoPlane.net.ID.Value={cargoPlane.net.ID.Value}");
#endif
		}

		private object CanLootEntity(BasePlayer player, SupplyDrop drop)
		{
			if (player == null || drop == null) return null;

			Configuration.SupplyInfo info;
			if (!_ownerToInfo.TryGetValue(drop.OwnerID, out info) || info.Loot.Access.AllPlayers) return null;

			var owner = drop.inventory.GetOwnerPlayer();
			if (owner == null || player.userID == owner.userID) return null;

			if ((info.Loot.Access.Teammates && IsTeammates(owner.userID, player.userID)) ||
			    (info.Loot.Access.Friends && IsFriends(owner.userID, player.userID)) ||
			    (info.Loot.Access.Clanmates && IsClanMember(owner.userID, player.userID)))
				return null;

			Reply(player, CannotLoot);
			return true;
		}

		#region EcoUILoot

		private object CanUILootSpawn(SupplyDrop drop)
		{
			if (drop == null) return null;

			if (_ownerToInfo.ContainsKey(drop.OwnerID))
				return true;

			return null;
		}

		#endregion

		#region Lootable

		private void OnPluginLoaded(Plugin plugin)
		{
			if (plugin.Name == "Loottable")
			{
				_hasLoottables = true;

				NextTick(UseLottable);
			}
		}

		private void OnPluginUnloaded(Plugin plugin)
		{
			if (plugin.Name == "Loottable")
			{
				_hasLoottables = false;

				NextTick(UseLottable);
			}
		}

		#endregion

		#region Fancy Drop

		private object ShouldFancyDrop(uint signalId)
		{
			if (_supplyToInfo.ContainsKey(signalId))
				return true;

			return null;
		}

		#endregion

		#endregion

		#region Commands

		[ConsoleCommand("hsupply.give")]
		private void CmdSupplyGive(ConsoleSystem.Arg arg)
		{
			if (!arg.IsAdmin) return;

			if (!arg.HasArgs(2))
			{
				SendReply(arg, $"Error syntax! Use: /{arg.cmd.FullName} [name/steamid] [supply name] [amount]");
				return;
			}

			var target = BasePlayer.FindAwakeOrSleeping(arg.Args[0]);
			if (target == null)
			{
				SendReply(arg, $"We can't find player with that name/ID! {arg.Args[0]}");
				return;
			}

			var supplyInfo = _config.Supplies.Find(x =>
				string.Equals(x.Name, arg.Args[1], StringComparison.CurrentCultureIgnoreCase));
			if (supplyInfo == null)
			{
				SendReply(arg, $"We can't find supply with that name! {arg.Args[1]}");
				return;
			}

			var amount = 1;
			if (arg.HasArgs(3))
				int.TryParse(arg.Args[2], out amount);

			var item = supplyInfo.Item.ToItem(amount);
			if (item == null) return;

			target.GiveItem(item, BaseEntity.GiveItemReason.PickedUp);
		}

		#endregion

		#region Drop Speed

		private class DropBehaviour : MonoBehaviour
		{
			private SupplyDrop sDrop;
			private Rigidbody rBody;
			private Transform transForm;

			private RaycastHit rayCastHit;

			private float heightAtPos;
			private float distToTarget;
			private bool DeployedChute;

			private BaseEntity parachute;
			private Vector3 velocityDrop;

			private float dropGroundDistance;
			private float dropVelocity;

			private void Awake()
			{
				sDrop = GetComponent<SupplyDrop>();
				rBody = GetComponent<Rigidbody>();
				transForm = sDrop.transform;
			}

			public void Init(Configuration.DropSpeed speed)
			{
				dropGroundDistance = Mathf.Max(speed.DropGroundDistance, 5);
				dropVelocity = Mathf.Max(speed.DropVelocity, 1);
			}

			private void Start()
			{
				sDrop.RemoveParachute();

				rBody.isKinematic = false;
				rBody.useGravity = true;
				rBody.mass = 1.25f;
				rBody.interpolation = RigidbodyInterpolation.Interpolate;
				rBody.drag = 0.1f;
				rBody.angularDrag = 0.1f;
				rBody.AddForce(Vector3.down * dropVelocity, ForceMode.Impulse);

				heightAtPos =
					Physics.Raycast(transForm.position + Vector3.down, Vector3.down, out rayCastHit, 500f, CAST_LAYERS,
						QueryTriggerInteraction.Collide)
						? rayCastHit.point.y
						: TerrainMeta.HeightMap.GetHeight(transForm.position);
			}

			private void Update()
			{
				distToTarget = transForm.position.y - heightAtPos;

				if (distToTarget < dropGroundDistance && distToTarget > 2f)
				{
					if (!DeployedChute)
					{
						parachute = GameManager.server.CreateEntity(PARACHUTE_PREFAB, transForm.position,
							Quaternion.identity);
						parachute.SetParent(sDrop, "parachute_attach");
						parachute.transform.localPosition = Vector3.zero;
						parachute.transform.localRotation = Quaternion.identity;
						parachute.enableSaving = false;
						parachute.Spawn();

						velocityDrop = rBody.velocity;

						DeployedChute = true;
					}

					rBody.velocity = Vector3.Lerp(velocityDrop, Vector3.zero,
						1f - Mathf.InverseLerp(0f, dropGroundDistance, distToTarget));
				}

				if (distToTarget < 1f)
				{
					if (DeployedChute)
					{
						sDrop.RemoveParachute();
						parachute.Kill();
					}

					DeployedChute = false;
				}
			}

			private void OnGroundCollision()
			{
				sDrop.RemoveParachute();
				parachute.Kill();
				Destroy(this);
			}
		}

		#endregion

		#region Utils

		private bool IsClanMember(ulong playerID, ulong targetID)
		{
			return Convert.ToBoolean(Clans?.Call("HasFriend", playerID, targetID));
		}

		private bool IsFriends(ulong playerID, ulong friendId)
		{
			return Convert.ToBoolean(Friends?.Call("AreFriends", playerID, friendId));
		}

		private static bool IsTeammates(ulong player, ulong friend)
		{
			var team = RelationshipManager.ServerInstance.FindPlayersTeam(player);
			return player != friend && team != null && team.members.Contains(friend);
		}

		private void SpawnSupplyDrop(Configuration.SupplyInfo info, Vector3 pos, Quaternion rot, ulong owner)
		{
#if TESTING
			Puts($"SpawnSupplyDrop: info={info?.ToString() ?? "null"}, pos={pos}, rot={rot}, owner={owner}");
#endif

			var drop = (SupplyDrop) GameManager.server.CreateEntity(DROP_PREFAB, pos, rot);
			if (drop == null)
			{
#if TESTING
				Puts("SpawnSupplyDrop: drop is null");
#endif
				return;
			}

			drop.OwnerID = info.OwnerID;

#if TESTING
			Puts($"SpawnSupplyDrop: drop.OwnerID={drop.OwnerID}");
#endif

			drop.enableSaving = false;

			drop.Spawn();

#if TESTING
			Puts($"SpawnSupplyDrop: drop.Spawned={drop.IsFullySpawned()}");
#endif

			NextTick(() =>
			{
#if TESTING
				PrintInventoryContents(drop.inventory, "Before DoPopulate");
#endif

				info.Loot?.DoPopulate(drop, owner);

#if TESTING
				PrintInventoryContents(drop.inventory, "After DoPopulate");
#endif

				if (info.Loot?.Speed?.Enabled == true)
					drop.gameObject
						.AddComponent<DropBehaviour>()?
						.Init(info.Loot.Speed);

#if TESTING
				timer.In(2, () =>
				{
					if (drop != null)
						PrintInventoryContents(drop.inventory, "2 seconds after DoPopulate");
				});
#endif
			});
		}

		private void UseLottable()
		{
			_hasLoottables = Loottable != null && Loottable.IsLoaded;

			if (_hasLoottables)
				_config.Supplies.ForEach(supply =>
				{
					var def = ItemManager.FindItemDefinition(supply.Item.ShortName);
					if (def == null) return;

					Loottable?.Call("AddCustomItem", this, def.itemid,
						supply.Item.Skin,
						supply.Item.DisplayName);
				});
		}

		private void TryCacheSupplies()
		{
			_config.Supplies.ForEach(supply =>
			{
				if (_supplyBySkinId.ContainsKey(supply.Item.Skin))
				{
					PrintError($"Found a duplicate skinid: {supply.Item.Skin}");
					return;
				}

				_supplyBySkinId[supply.Item.Skin] = supply;

				if (supply.Drop.Enabled)
					supply.Drop.Containers.ForEach(cont =>
					{
						if (!_containerToSupplies.ContainsKey(cont.PrefabName))
							_containerToSupplies[cont.PrefabName] = new List<Configuration.SupplyInfo>();

						if (!_containerToSupplies[cont.PrefabName].Contains(supply))
							_containerToSupplies[cont.PrefabName].Add(supply);
					});

				supply.GenerateOwnerId();
			});
		}

		private Configuration.SupplyInfo FindSupplyInfo(ulong skin)
		{
			Configuration.SupplyInfo info;
			return _supplyBySkinId.TryGetValue(skin, out info) ? info : null;
		}

		#endregion

		#region Lang

		private const string
			CannotLoot = "CannotLoot";

		protected override void LoadDefaultMessages()
		{
			lang.RegisterMessages(new Dictionary<string, string>
			{
				[CannotLoot] = "You don't have permission to loot this drop!"
			}, this);
		}

		private string Msg(string key, string userid = null, params object[] obj)
		{
			return string.Format(lang.GetMessage(key, this, userid), obj);
		}

		private string Msg(BasePlayer player, string key, params object[] obj)
		{
			return string.Format(_instance.lang.GetMessage(key, _instance, player.UserIDString), obj);
		}

		private void Reply(BasePlayer player, string key, params object[] obj)
		{
			SendReply(player, Msg(player, key, obj));
		}

		#endregion

		#region Testing functions

#if TESTING
		private void DebugMessage(string format, long time)
		{
			PrintWarning(format, time);
		}

		private class StopwatchWrapper : IDisposable
		{
			public StopwatchWrapper(string format)
			{
				Sw = Stopwatch.StartNew();
				Format = format;
			}

			public static Action<string, long> OnComplete { private get; set; }

			private string Format { get; }
			private Stopwatch Sw { get; }

			public long Time { get; private set; }

			public void Dispose()
			{
				Sw.Stop();
				Time = Sw.ElapsedMilliseconds;
				OnComplete(Format, Time);
			}
		}

		private void PrintInventoryContents(ItemContainer inventory, string title)
		{
			if (inventory == null)
			{
				Puts($"PrintInventoryContents. {title}: Inventory is null");
				return;
			}

			var sb = $"PrintInventoryContents. {title}: items in inventory:";
			inventory.itemList.ForEach(item =>
			{
				if (item != null) sb += $"\n – {item.info.shortname} x {item.amount}";
			});

			sb += $"\ncontainer size: {inventory.capacity}";

			Puts(sb);
		}

#endif

		#endregion
	}
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху