C#:
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Ext.ChaosNPC;
using Rust;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
namespace Oxide.Plugins {
[Info("HeliRefuel", "Sempai#3239", "2.1.8")]
[Description("A mini-event where a helicopter populated with NPCs needs to land to refuel")]
class HeliRefuel : RustPlugin, IChaosNPCPlugin {
#region Fields
[PluginReference]
Plugin PilotEject;
private static List<ConfigData.LocationOptions.Monument.CustomOptions> RefuelPoints;
protected static HeliRefuel Instance {
get;
private set;
}
private static NavMeshHit navHit;
private static RaycastHit raycastHit;
private bool isSubscribed = true;
private Hash<BasePlayer, Vector3> velocityInheritance = new Hash<BasePlayer, Vector3>();
private List<CustomScientistNPC> customNpcs = new List<CustomScientistNPC>();
private const string HELICOPTER_PREFAB = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
private const string CHAIR_PREFAB = "assets/prefabs/deployable/chair/chair.deployed.prefab";
private const string ELITE_CRATE_PREFAB = "assets/bundled/prefabs/radtown/crate_elite.prefab";
private const string HELI_CRATE_PREFAB = "assets/prefabs/npc/patrol helicopter/heli_crate.prefab";
private const string ADMIN_PERM = "helirefuel.admin";
private const int VEHICLE_LAYER = 1 << 13 | 1 << 27;
private const int TARGET_LAYERS = ~(1 << 10 | 1 << 12 | 1 << 13 | 1 << 17 | 1 << 18 | 1 << 27 | 1 << 28 | 1 << 29);
#endregion
#region Oxide Hooks
private void Loaded() {
Instance = this;
lang.RegisterMessages(Messages, this);
permission.RegisterPermission(ADMIN_PERM, this);
}
private void OnServerInitialized() {
PrintWarning("\n-----------------------------\n" + " Author - Sempai#3239\n" + " VK - https://vk.com/rustnastroika/n" + " Forum - https://darkplugins.ru/n" + " Discord - https://discord.gg/5DPTsRmd3G/n" + "-----------------------------");
PopulateRefuelLocations();
ToggleSubscriptions(false);
if (configData.Automation.Enabled) RunAutomatedEvent();
}
private void OnEntityTakeDamage(BaseNetworkable networkable, HitInfo info) {
if (networkable == null || info == null) return;
BaseMountable mountable = networkable as BaseMountable;
if (mountable != null) {
if (mountable.GetComponentInParent<RefuelHelicopter>()) NullifyDamage(info);
}
RefuelHelicopter refuelHelicopter = networkable is BaseHelicopter ? networkable.GetComponent<RefuelHelicopter>() : null;
if (refuelHelicopter != null) refuelHelicopter.OnDamage(info);
}
private void OnEntityKill(BaseHelicopter helicopter) {
RefuelHelicopter refuelHelicopter = helicopter?.GetComponent<RefuelHelicopter>();
if (refuelHelicopter != null) {
refuelHelicopter.destroyKill = false;
UnityEngine.Object.Destroy(refuelHelicopter);
}
}
private void OnEntityKill(CustomScientistNPC customNpc) => customNpcs.Remove(customNpc);
private void OnHelicopterRetire(PatrolHelicopterAI helicopterAI) {
RefuelHelicopter refuelHelicopter = helicopterAI?.helicopterBase?.GetComponent<RefuelHelicopter>();
if (refuelHelicopter != null) {
if (refuelHelicopter.hasValidLocation || refuelHelicopter.isLanding || refuelHelicopter.isRefueling) {
InvokeHandler.CancelInvoke(refuelHelicopter, refuelHelicopter.ResetStateToPatrol);
refuelHelicopter.ResetStateToPatrol();
}
refuelHelicopter.isRetiring = true;
refuelHelicopter.destroyKill = false;
}
}
private object OnNpcTarget(ScientistNPC attacker, CustomScientistNPC customNpc) => customNpcs.Contains(customNpc) ? (object)true : null;
private object CanBradleyApcTarget(BradleyAPC bradleyApc, CustomScientistNPC customNpc) => customNpcs.Contains(customNpc) ? (object)false : null;
private object OnTurretTarget(NPCAutoTurret turret, CustomScientistNPC customNpc) => customNpcs.Contains(customNpc) ? (object)true : null;
private object CanBeTargeted(CustomScientistNPC customNpc, MonoBehaviour behaviour) {
if (customNpcs.Contains(customNpc)) {
if (((behaviour is AutoTurret) || (behaviour is GunTrap) || (behaviour is FlameTurret)) && configData.NPC.TargetedByTurrets) return null;
return false;
}
return null;
}
private void OnEntityDeath(CustomScientistNPC customNpc, HitInfo info) {
for (int i = 0; i < RefuelHelicopter.allHelicopters.Count; i++) {
RefuelHelicopter refuelHelicopter = RefuelHelicopter.allHelicopters[i];
if (refuelHelicopter.NPCs.Contains(customNpc)) {
refuelHelicopter.OnNPCKilled(customNpc);
return;
}
}
}
private void OnPlayerCorpseSpawned(CustomScientistNPC customNpc, BaseCorpse corpse) {
if (customNpc == null || corpse == null) return;
Vector3 velocity;
if (velocityInheritance.TryGetValue(customNpc, out velocity)) {
velocityInheritance.Remove(customNpc);
NextTick(() => corpse?.GetComponent<Rigidbody>()?.AddForce(velocity, ForceMode.VelocityChange));
}
}
private void Unload() {
RefuelPoints.Clear();
for (int i = RefuelHelicopter.allHelicopters.Count - 1; i >= 0 ; i--) {
RefuelHelicopter refuelHelicopter = RefuelHelicopter.allHelicopters[i];
refuelHelicopter.killSilent = true;
refuelHelicopter.destroyKill = false;
UnityEngine.Object.Destroy(refuelHelicopter);
}
configData = null;
Instance = null;
}
#endregion
#region Functions
private void ToggleSubscriptions(bool subscribe) {
if (subscribe == isSubscribed) return;
if (subscribe) {
Subscribe(nameof(OnEntityTakeDamage));
Subscribe(nameof(OnNpcTarget));
Subscribe(nameof(CanBradleyApcTarget));
Subscribe(nameof(OnTurretTarget));
Subscribe(nameof(OnEntityKill));
}
else {
Unsubscribe(nameof(OnEntityTakeDamage));
Unsubscribe(nameof(OnNpcTarget));
Unsubscribe(nameof(CanBradleyApcTarget));
Unsubscribe(nameof(OnTurretTarget));
Unsubscribe(nameof(OnEntityKill));
}
isSubscribed = subscribe;
}
private void RunAutomatedEvent() {
timer.In(UnityEngine.Random.Range(configData.Automation.Minimum, configData.Automation.Maximum), () => {
if (BasePlayer.activePlayerList.Count >= configData.Automation.RequiredPlayers) SpawnEntity();
RunAutomatedEvent();
}
);
}
private void SpawnEntity(Vector3 positionOverride = default(Vector3)) {
BaseHelicopter helicopter = GameManager.server.CreateEntity(HELICOPTER_PREFAB) as BaseHelicopter;
helicopter.enableSaving = false;
helicopter.Spawn();
Broadcast("Notification.OnSpawned");
NextTick(() => {
if (helicopter == null) {
PrintError("[Plugin Conflict] Another plugin on your server has destroyed the helicopter when it spawned");
return;
}
helicopter.transform.position = positionOverride == default(Vector3) ? RandomPointOnWorldBounds() : positionOverride;
helicopter.myAI.State_Patrol_Enter();
helicopter.gameObject.AddComponent<RefuelHelicopter>();
}
);
}
private Vector3 RandomPointOnWorldBounds() {
float size = TerrainMeta.Size.x / 2f;
Vector3 position;
if (UnityEngine.Random.value > 0.5f) position = new Vector3(UnityEngine.Random.value > 0.5f ? size : -size, 0, UnityEngine.Random.Range(-size, size));
else position = new Vector3(UnityEngine.Random.Range(-size, size), 0, UnityEngine.Random.value > 0.5f ? size : -size);
position.y = TerrainMeta.HeightMap.GetHeight(position) + 30f;
return position;
}
private void PopulateRefuelLocations() {
RefuelPoints = new List<ConfigData.LocationOptions.Monument.CustomOptions>(configData.Location.Custom.Where(x => x.Enabled));
ConfigData.LocationOptions.Monument config = configData.Location.Monuments;
MonumentInfo[] monuments = UnityEngine.Object.FindObjectsOfType<MonumentInfo>();
for (int i = 0; i < monuments.Length; i++) {
MonumentInfo monument = monuments[i];
if (monument.name.Contains("harbor_1", CompareOptions.IgnoreCase)) {
if (config.HarbourLarge.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.LargeHarbour"), Position = monument.transform.TransformPoint(config.HarbourLarge.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.HarbourLarge.Rotation)
}
);
config.HarbourLarge.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("harbor_2", CompareOptions.IgnoreCase)) {
if (config.HarbourSmall.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.SmallHarbour"), Position = monument.transform.TransformPoint(config.HarbourSmall.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.HarbourSmall.Rotation)
}
);
config.HarbourSmall.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("airfield_1", CompareOptions.IgnoreCase)) {
if (config.Airfield.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.Airfield"), Position = monument.transform.TransformPoint(config.Airfield.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.Airfield.Rotation)
}
);
config.Airfield.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("launch_site_1", CompareOptions.IgnoreCase)) {
if (config.LaunchSite.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.LaunchSite"), Position = monument.transform.TransformPoint(config.LaunchSite.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.LaunchSite.Rotation)
}
);
config.LaunchSite.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("oilrigai2", CompareOptions.IgnoreCase)) {
if (config.LargeOilRig.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.LargeOilRig"), Position = monument.transform.TransformPoint(config.LargeOilRig.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.LargeOilRig.Rotation), IsOilRig = true
}
);
config.LargeOilRig.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("oilrigai", CompareOptions.IgnoreCase)) {
if (config.SmallOilRig.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.SmallOilRig"), Position = monument.transform.TransformPoint(config.SmallOilRig.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.SmallOilRig.Rotation), IsOilRig = true
}
);
config.SmallOilRig.Monument.Add(monument);
}
continue;
}
if (monument.name.Contains("powerplant_1", CompareOptions.IgnoreCase)) {
if (config.Powerplant.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.PowerPlant"), Position = monument.transform.TransformPoint(config.Powerplant.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.Powerplant.Rotation)
}
);
config.Powerplant.Monument.Add(monument);
}
}
if (monument.name.Contains("military_tunnel_1", CompareOptions.IgnoreCase)) {
if (config.MilitaryTunnels.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.MilitaryTunnel"), Position = monument.transform.TransformPoint(config.MilitaryTunnels.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.MilitaryTunnels.Rotation)
}
);
config.MilitaryTunnels.Monument.Add(monument);
}
}
if (monument.name.Contains("junkyard_1", CompareOptions.IgnoreCase)) {
if (config.Junkyard.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.Junkyard"), Position = monument.transform.TransformPoint(config.Junkyard.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.Junkyard.Rotation)
}
);
config.Junkyard.Monument.Add(monument);
}
}
if (monument.name.Contains("water_treatment_plant_1", CompareOptions.IgnoreCase)) {
if (config.WaterTreatment.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.WaterTreatment"), Position = monument.transform.TransformPoint(config.WaterTreatment.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.WaterTreatment.Rotation)
}
);
config.WaterTreatment.Monument.Add(monument);
}
}
if (monument.name.Contains("trainyard_1", CompareOptions.IgnoreCase)) {
if (config.TrainYard.Enabled) {
RefuelPoints.Add(new ConfigData.LocationOptions.Monument.CustomOptions() {
Name = Message("Monument.TrainYard"), Position = monument.transform.TransformPoint(config.TrainYard.Position), Rotation = monument.transform.rotation * Quaternion.Euler(config.TrainYard.Rotation)
}
);
config.TrainYard.Monument.Add(monument);
}
}
}
Puts($"Found {RefuelPoints.Count} valid refuel points; {RefuelPoints.Select(x => Messages.ContainsKey(x.Name) ? Message(x.Name) : x.Name).ToSentence()}");
}
private static void Broadcast(string key, params object[] args) {
foreach (BasePlayer player in BasePlayer.activePlayerList) player.ChatMessage(args?.Length > 0 ? string.Format(Instance.Message(key, player.userID), args) : Instance.Message(key, player.userID));
}
private static void NullifyDamage(HitInfo info) {
info.damageTypes = new DamageTypeList();
info.HitEntity = null;
info.HitMaterial = 0;
info.PointStart = Vector3.zero;
}
private static void ClearContainer(ItemContainer container) {
if (container == null || container.itemList == null) return;
for (int i = container.itemList.Count - 1; i >= 0; i--) {
Item item = container.itemList[i];
item.RemoveFromContainer();
item.Remove(0f);
}
}
private static void PopulateLoot(LootContainer container) {
if (container == null || container.inventory == null) return;
ClearContainer(container.inventory);
int amount = UnityEngine.Random.Range(configData.Loot.MinimumItems, configData.Loot.MaximumItems);
List<ConfigData.LootOptions.LootItem> list = Pool.GetList<ConfigData.LootOptions.LootItem>();
list.AddRange(configData.Loot.Items);
for (int i = 0; i < amount; i++) {
if (list.Count == 0) list = new List<ConfigData.LootOptions.LootItem>(configData.Loot.Items);
ConfigData.LootOptions.LootItem lootItem = list.GetRandom();
list.Remove(lootItem);
Item item = ItemManager.CreateByName(lootItem.Name, UnityEngine.Random.Range(lootItem.Minimum, lootItem.Maximum), lootItem.Skin);
item.MoveToContainer(container.inventory);
}
Pool.FreeList(ref list);
}
private bool IsRefuelHelicopter(BaseHelicopter helicopter) => helicopter.GetComponent<RefuelHelicopter>() != null;
#endregion
#region CustomNPC
public bool InitializeStates(BaseAIBrain customNPCBrain) => false;
public bool WantsToPopulateLoot(CustomScientistNPC customNpc, NPCPlayerCorpse npcplayerCorpse) => false;
public byte[] GetCustomDesign() => null;
#endregion
#region Component
public class RefuelHelicopter : MonoBehaviour {
internal static List<RefuelHelicopter> allHelicopters = new List<RefuelHelicopter>();
protected BaseHelicopter Helicopter {
get;
private set;
}
protected PatrolHelicopterAI AI {
get;
private set;
}
protected List<BaseMountable> Mounts = new List<BaseMountable>();
internal List<CustomScientistNPC> NPCs = new List<CustomScientistNPC>();
protected List<LootContainer> Containers = new List<LootContainer>();
internal bool hasValidLocation = false;
internal bool isLanding = false;
internal bool isRefueling = false;
internal bool isRetiring = false;
internal bool killSilent = false;
private bool isOilRig = false;
private bool enemiesInVicinity = false;
private bool isDying = false;
private bool hasDroppedOnDeath = false;
internal bool destroyKill = true;
private float actualHealth;
private string locationName;
private Vector3 targetLocation;
private Quaternion targetRotation;
private Vector3 hoverDistance = new Vector3(0f, 20f, 0f);
private Vector3 landingLocation;
private Quaternion landingRotation;
private MapMarkerGenericRadius mapMarker;
private float nextValidationTime;
private const float TIME_TO_LAND = 5f;
private float landTimeTaken = 0f;
private const string MAP_MARKER_PREFAB = "assets/prefabs/tools/map/genericradiusmarker.prefab";
private List<KeyValuePair<Vector3, float>> mountLocations = new List<KeyValuePair<Vector3, float>> {
new KeyValuePair<Vector3, float>(new Vector3(-0.6f, 0.65f, 2.8f), 0f), new KeyValuePair<Vector3, float>(new Vector3(0.6f, 0.65f, 2.8f), 0f), new KeyValuePair<Vector3, float>(new Vector3(-0.8f, 0.68f, -0.5f), -90f), new KeyValuePair<Vector3, float>(new Vector3(0.8f, 0.68f, -0.5f), 90f), new KeyValuePair<Vector3, float>(new Vector3(-0.3f, 0.68f, 0.6f), 0f), new KeyValuePair<Vector3, float>(new Vector3(0.3f, 0.68f, 0.6f), 0f)
}
;
private void Awake() {
Helicopter = GetComponent<BaseHelicopter>();
AI = GetComponent<PatrolHelicopterAI>();
Helicopter.maxCratesToSpawn = 0;
actualHealth = Helicopter.health;
InvokeHandler.Invoke(this, TriggerRefuel, UnityEngine.Random.Range(configData.Helicopter.MinRefuelTime, configData.Helicopter.MaxRefuelTime));
CreateMountPoints();
CreateNPCs();
allHelicopters.Add(this);
Instance.ToggleSubscriptions(true);
}
private void OnDestroy() {
if (mapMarker != null && !mapMarker.IsDestroyed) mapMarker.Kill();
if (!isDying) {
for (int i = 0; i < NPCs?.Count; i++) {
CustomScientistNPC customNpc = NPCs[i];
if (customNpc != null && !customNpc.IsDestroyed) customNpc.Kill();
}
}
for (int i = 0; i < Mounts?.Count; i++) {
BaseMountable mountable = Mounts[i];
if (mountable != null && !mountable.IsDestroyed) mountable.Kill();
}
if (killSilent) {
for (int i = 0; i < Containers?.Count; i++) {
LootContainer container = Containers[i];
if (container != null && !container.IsDestroyed) {
ClearContainer(container.inventory);
container.Kill();
}
}
}
allHelicopters?.Remove(this);
if (allHelicopters.Count == 0) Instance?.ToggleSubscriptions(false);
if (destroyKill && Helicopter != null && !Helicopter.IsDestroyed) Helicopter.Kill();
}
private void Update() {
if (!hasValidLocation || (AI.enabled && isRetiring)) return;
if (isLanding) {
landTimeTaken += Time.deltaTime;
float delta = Mathf.InverseLerp(0f, TIME_TO_LAND, landTimeTaken);
transform.position = Vector3.Lerp(landingLocation, targetLocation, delta);
transform.rotation = Quaternion.Slerp(landingRotation, targetRotation, delta);
if (delta >= 1f) {
isLanding = false;
hasValidLocation = false;
isRefueling = true;
DismountAll();
CreateLoot();
CreateMapMarker();
if (configData.Notification.BeginRefuelling) Broadcast("Notification.BeginRefuelling2", locationName);
InvokeHandler.Invoke(this, ResetStateToPatrol, configData.Helicopter.RefuelTime);
}
return;
}
float distanceFromTarget = Vector3Ex.Distance2D(transform.position, targetLocation);
if (Time.realtimeSinceStartup > nextValidationTime) {
if (!IsClearToLand()) {
hasValidLocation = false;
if (configData.Notification.IntendedDestination) Broadcast("Notification.LocationOccupied");
InvokeHandler.Invoke(this, FindTargetLocation, 5f);
return;
}
FindEnemiesInVicinity();
nextValidationTime = Time.realtimeSinceStartup + 5f;
}
AIUpdate();
SetTargetDestination(targetLocation + hoverDistance);
if (enemiesInVicinity) return;
if (distanceFromTarget < 5) {
isLanding = true;
landTimeTaken = 0f;
AI.enabled = false;
AI.ClearAimTarget();
landingLocation = transform.position;
landingRotation = transform.rotation;
for (int i = 0; i < NPCs.Count; i++) {
CustomScientistNPC customNpc = NPCs[i];
if (isOilRig) {
customNpc.NavAgent.areaMask = 25;
customNpc.NavAgent.agentTypeID = 0;
}
else {
customNpc.NavAgent.areaMask = 1;
customNpc.NavAgent.agentTypeID = -1372625422;
}
}
}
}
private void SetTargetDestination(Vector3 targetDest) {
AI.destination = targetDest;
float distance = Vector3.Distance(targetDest, transform.position);
if (distance > 30) AI.SetIdealRotation(AI.GetYawRotationTo(targetDest), -1f);
AI.targetThrottleSpeed = 0.25f;
}
internal void ResetStateToPatrol() {
if (mapMarker != null && !mapMarker.IsDestroyed) mapMarker.Kill();
if (configData.NPC.Amount > 0 && NPCs.Count == 0) {
if (configData.Helicopter.SelfDestruct) {
if (configData.Helicopter.SelfDestructTime == 0) configData.Helicopter.SelfDestructTime = 300;
if (!InvokeHandler.IsInvoking(this, Helicopter.DieInstantly)) {
Broadcast("Notification.SelfDestruct", configData.Helicopter.SelfDestructTime);
InvokeHandler.Invoke(this, Helicopter.DieInstantly, configData.Helicopter.SelfDestructTime);
}
return;
}
else {
Broadcast("Notification.EventFinished");
destroyKill = false;
InvokeHandler.Invoke(this, ()=> {
AI.enabled = true;
AI.ClearAimTarget();
AI.State_Patrol_Enter();
Destroy(this);
}
, 5f);
return;
}
}
if (configData.Notification.FinishRefuelling) Broadcast("Notification.FinishRefuellingPre3", locationName, configData.Helicopter.IdleTime);
for (int i = 0; i < NPCs.Count; i++) {
CustomScientistNPC customNpc = NPCs[i];
ChaosNPC.SetDestination(customNpc, Helicopter.transform.position, () => MountAny(customNpc));
}
PackLoot();
InvokeHandler.Invoke(this, ResetState, configData.Helicopter.IdleTime);
}
private void ResetState() {
for (int i = 0; i < NPCs.Count; i++) {
CustomScientistNPC customNpc = NPCs[i];
if (customNpc != null && !customNpc.isMounted) {
MountAny(customNpc);
}
}
isRefueling = false;
enabled = true;
AI.enabled = true;
AI.ClearAimTarget();
AI.State_Patrol_Enter();
hasValidLocation = false;
if (configData.Notification.FinishRefuelling) Broadcast("Notification.FinishRefuelling2", locationName);
InvokeHandler.Invoke(this, TriggerRefuel, UnityEngine.Random.Range(configData.Helicopter.MinRefuelTime, configData.Helicopter.MaxRefuelTime));
}
private void AIUpdate() {
AI.MoveToDestination();
AI.UpdateRotation();
AI.UpdateSpotlight();
AI.AIThink();
AI.DoMachineGuns();
}
private bool IsClearToLand() => !Vis.AnyColliders(targetLocation, 10f, VEHICLE_LAYER);
private void FindEnemiesInVicinity() {
enemiesInVicinity = false;
List<BasePlayer> players = Pool.GetList<BasePlayer>();
Vis.Entities(targetLocation, 30f, players);
players.RemoveAll(x => x.IsNpc || x.GetThreatLevel() <= 0.5f || !AI.PlayerVisible(x));
if (players.Count > 0) {
enemiesInVicinity = true;
AI._targetList.Clear();
players.ForEach(x => AI._targetList.Add(new PatrolHelicopterAI.targetinfo(x, x)));
}
Pool.FreeList(ref players);
}
internal void TriggerRefuel() {
if (isRetiring) return;
FindTargetLocation();
if (!hasValidLocation) InvokeHandler.Invoke(this, TriggerRefuel, UnityEngine.Random.Range(configData.Helicopter.MinRefuelTime, configData.Helicopter.MaxRefuelTime));
else {
if (configData.Notification.IntendedDestination) Broadcast("Notification.TargetLocation", locationName);
nextValidationTime = Time.realtimeSinceStartup + 20f;
AI.ClearAimTarget();
}
}
private void FindTargetLocation() {
targetLocation = Vector3.zero;
targetRotation = Quaternion.identity;
if (configData.Location.RefuelDetermination == "Closest") {
IOrderedEnumerable<ConfigData.LocationOptions.Monument.CustomOptions> points = RefuelPoints.OrderBy(x => Vector3.Distance(x.Position, transform.position));
for (int i = 0; i < points.Count();
i++) {
ConfigData.LocationOptions.Monument.CustomOptions point = points.ElementAt(i);
if (Vis.AnyColliders(point.Position, 10f, VEHICLE_LAYER)) continue;
locationName = point.Name;
targetLocation = point.Position;
targetRotation = Quaternion.Euler(0f, point.Rotation.y, 0f);
hasValidLocation = true;
isOilRig = point.IsOilRig;
return;
}
}
else {
List<ConfigData.LocationOptions.Monument.CustomOptions> points = new List<ConfigData.LocationOptions.Monument.CustomOptions>(RefuelPoints);
for (int i = 0; i < points.Count; i++) {
ConfigData.LocationOptions.Monument.CustomOptions point = points.GetRandom();
if (Vis.AnyColliders(point.Position, 10f, VEHICLE_LAYER)) {
points.Remove(point);
continue;
}
locationName = point.Name;
targetLocation = point.Position;
targetRotation = Quaternion.Euler(0f, point.Rotation.y, 0f);
hasValidLocation = true;
isOilRig = point.IsOilRig;
return;
}
}
hasValidLocation = false;
print($"[HeliRefuel] - Unable to land for refuel.All refuel points are occupied by players or vehicles");
}
private void CreateMapMarker() {
if (!configData.Helicopter.UseMapMarker) return;
mapMarker = GameManager.server.CreateEntity(MAP_MARKER_PREFAB, transform.position) as MapMarkerGenericRadius;
mapMarker.enableSaving = false;
mapMarker.Spawn();
mapMarker.color1 = mapMarker.color2 = ConvertToColor(configData.Helicopter.MarkerColor);
mapMarker.alpha = configData.Helicopter.MarkerOpacity;
mapMarker.radius = configData.Helicopter.MarkerSize;
mapMarker.SendUpdate();
}
private Color ConvertToColor(string color) {
if (color.StartsWith("#")) color = color.Substring(1);
int red = int.Parse(color.Substring(0, 2), NumberStyles.AllowHexSpecifier);
int green = int.Parse(color.Substring(2, 2), NumberStyles.AllowHexSpecifier);
int blue = int.Parse(color.Substring(4, 2), NumberStyles.AllowHexSpecifier);
return new Color((float)red / 255, (float)green / 255, (float)blue / 255);
}
#region Death and Damage
internal void OnDamage(HitInfo info) {
actualHealth -= info.damageTypes.Total();
if (actualHealth < 0f) {
if (isRefueling || isLanding) {
OnDeath(info);
NullifyDamage(info);
}
else {
if (isDying && info.damageTypes.GetMajorityDamageType() == DamageType.Generic) DropOnDeath(info);
if (!isDying) CrashToDeath();
}
}
}
internal void OnDeath(HitInfo info) {
if (isDying) return;
isDying = true;
if (isRefueling || isLanding) {
AI.moveSpeed = 0;
typeof(PatrolHelicopterAI).GetField("_lastMoveDir", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).SetValue(AI, Vector3.zero);
}
DropOnDeath(info);
Interface.CallHook("OnEntityDeath", Helicopter);
Helicopter.DieInstantly();
}
internal void CrashToDeath() {
isDying = true;
AI.CriticalDamage();
}
private void DropOnDeath(HitInfo info) {
if (hasDroppedOnDeath) return;
hasDroppedOnDeath = true;
if (!isRefueling) {
if (configData.Helicopter.DropCratesOnDeath) DropLoot();
if (configData.Helicopter.DropNPCsOnDeath) {
Vector3 direction = (AI.GetLastMoveDir() * AI.GetMoveSpeed()) * 0.75f;
for (int i = NPCs.Count - 1; i >= 0; i--) {
CustomScientistNPC customNpc = NPCs[i];
if (customNpc != null && !customNpc.IsDestroyed) {
Instance.velocityInheritance[customNpc] = direction + (UnityEngine.Random.onUnitSphere * UnityEngine.Random.Range(2f, 5f));
DismountObject(customNpc, true);
customNpc.Die(new HitInfo(info != null ? info.Initiator : Helicopter, customNpc, DamageType.Explosion, 1000f));
}
}
}
else {
for (int i = NPCs.Count - 1; i >= 0; i--) {
CustomScientistNPC customNpc = NPCs[i];
if (customNpc != null && !customNpc.IsDestroyed) {
DismountObject(customNpc, true);
customNpc.Kill();
}
}
}
}
else {
if (configData.Helicopter.DropCratesOnDeathLanded) DropLoot();
}
}
#endregion
#region NPCs and Mounting
private void CreateMountPoints() {
for (int i = 0; i < Mathf.Clamp(configData.NPC.Amount, 0, 6);
i++) {
BaseMountable mountable = GameManager.server.CreateEntity(CHAIR_PREFAB, transform.position) as BaseMountable;
mountable.enableSaving = false;
mountable.isMobile = true;
mountable.skinID = (ulong)1169930802;
mountable.Spawn();
Destroy(mountable.GetComponent<DestroyOnGroundMissing>());
Destroy(mountable.GetComponent<GroundWatch>());
mountable.GetComponent<MeshCollider>().convex = true;
mountable.SetParent(Helicopter);
KeyValuePair<Vector3, float> mountOffset = mountLocations[i];
mountable.transform.localPosition = mountOffset.Key;
mountable.transform.localRotation = Quaternion.Euler(0, mountOffset.Value, 0);
Mounts.Add(mountable);
}
}
private void CreateNPCs() {
configData.NPC.CustomNPCSettings.EnableNavMesh = false;
configData.NPC.CustomNPCSettings.CanUseWeaponMounted = false;
configData.NPC.CustomNPCSettings.EquipWeapon = false;
for (int i = 0; i < Mathf.Clamp(configData.NPC.Amount, 0, 6);
i++) {
CustomScientistNPC customNpc = ChaosNPC.SpawnNPC(Instance, transform.position, configData.NPC.CustomNPCSettings);
if (customNpc != null) {
Instance.customNpcs.Add(customNpc);
NPCs.Add(customNpc);
MountAny(customNpc);
}
}
}
internal void MountAny(CustomScientistNPC customNpc) {
for (int i = 0; i < Mounts.Count; i++) {
BaseMountable mountable = Mounts[i];
if (!mountable.IsMounted()) {
customNpc.SetPaused(true);
customNpc.HolsterWeapon();
MountObject(customNpc, mountable);
return;
}
}
}
private void MountObject(CustomScientistNPC customNpc, BaseMountable mountable) {
customNpc.EnsureDismounted();
mountable._mounted = customNpc;
customNpc.MountObject(mountable, 0);
customNpc.MovePosition(mountable.mountAnchor.transform.position);
customNpc.transform.rotation = mountable.mountAnchor.transform.rotation;
customNpc.ServerRotation = mountable.mountAnchor.transform.rotation;
customNpc.OverrideViewAngles(mountable.mountAnchor.transform.rotation.eulerAngles);
customNpc.eyes.NetworkUpdate(mountable.mountAnchor.transform.rotation);
customNpc.ClientRPCPlayer(null, customNpc, "ForcePositionTo", mountable.transform.position);
mountable.SetFlag(BaseEntity.Flags.Busy, true, false);
}
private void DismountAll() {
for (int i = 0; i < NPCs.Count; i++) {
DismountObject(NPCs[i]);
}
}
private void DismountObject(CustomScientistNPC customNpc, bool isDead = false) {
if (!customNpc.isMounted) return;
BaseMountable mountable = customNpc.GetMounted();
Vector3 dismountPosition = transform.position + (transform.forward * UnityEngine.Random.Range(-2f, 2f)) + ((UnityEngine.Random.value > 0.5f ? transform.right : -transform.right) * 3f);
if (NavMesh.SamplePosition(dismountPosition, out navHit, 3f, -1)) dismountPosition = navHit.position;
customNpc.mounted.Set(null);
customNpc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
mountable._mounted = null;
mountable.SetFlag(BaseEntity.Flags.Busy, false, false);
customNpc.transform.position = dismountPosition;
customNpc.transform.rotation = Quaternion.identity;
customNpc.NavAgent.Warp(dismountPosition);
customNpc.EquipWeapon();
ChaosNPC.SetRoamHomePosition(customNpc, customNpc.transform.position);
customNpc.SetPaused(false);
}
internal void OnNPCKilled(CustomScientistNPC customNpc) {
if (killSilent) return;
NPCs.Remove(customNpc);
if (!isDying && NPCs.Count == 0) {
if (!isRefueling) CrashToDeath();
else {
InvokeHandler.CancelInvoke(this, ResetStateToPatrol);
ResetStateToPatrol();
}
}
}
#endregion
#region Loot
private void CreateLoot() {
int amount = UnityEngine.Random.Range(configData.Loot.MinimumCrates, configData.Loot.MaximumCrates);
for (int i = 0; i < amount; i++) {
LootContainer container = CreateContainer();
if (container != null) {
Rigidbody rigidbody = container.gameObject.GetComponent<Rigidbody>();
if (rigidbody != null) rigidbody.useGravity = false;
Containers.Add(container);
}
}
}
private void DropLoot() {
int amount = UnityEngine.Random.Range(configData.Loot.MinimumCrates, configData.Loot.MaximumCrates);
Vector3 direction = (AI.GetLastMoveDir() * AI.GetMoveSpeed()) * 0.75f;
for (int i = 0; i < amount; i++) {
LootContainer container = CreateContainer(true);
if (container != null) {
container.Invoke(container.RemoveMe, 1800f);
Rigidbody rigidbody = container.gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rigidbody.mass = 2f;
rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
rigidbody.velocity = direction + (UnityEngine.Random.onUnitSphere * UnityEngine.Random.Range(1f, 3f));
rigidbody.angularVelocity = Vector3Ex.Range(-1.75f, 1.75f);
rigidbody.drag = 0.5f * (rigidbody.mass / 5f);
rigidbody.angularDrag = 0.2f * (rigidbody.mass / 5f);
FireBall fireBall = GameManager.server.CreateEntity(Helicopter.fireBall.resourcePath) as FireBall;
if (fireBall) {
fireBall.SetParent(container, false, false);
fireBall.Spawn();
fireBall.GetComponent<Rigidbody>().isKinematic = true;
fireBall.GetComponent<Collider>().enabled = false;
}
container.SendMessage("SetLockingEnt", fireBall.gameObject, SendMessageOptions.DontRequireReceiver);
}
}
}
private LootContainer CreateContainer(bool ignoreRaycast = false) {
string prefab = UnityEngine.Random.Range(0, 1) == 1 ? ELITE_CRATE_PREFAB : HELI_CRATE_PREFAB;
Vector2 random = UnityEngine.Random.insideUnitCircle.normalized;
Vector3 rayPosition = transform.position + (new Vector3(random.x, 0f, random.y) * 6f) + (Vector3.up * 5f);
if (ignoreRaycast || Physics.Raycast(new Ray(rayPosition, Vector3.down), out raycastHit, 10f, TARGET_LAYERS, QueryTriggerInteraction.Ignore)) {
Vector3 point = ignoreRaycast ? rayPosition : raycastHit.point;
LootContainer container = GameManager.server.CreateEntity(prefab, point, Quaternion.Euler(0f, UnityEngine.Random.Range(0, 360), 0f)) as LootContainer;
container.enableSaving = false;
container.Spawn();
if (configData.Loot.CustomLoot) PopulateLoot(container);
return container;
}
return null;
}
private void PackLoot() {
for (int i = Containers.Count - 1; i >= 0; i--) {
LootContainer container = Containers[i];
if (container != null && !container.IsDestroyed) {
Containers.Remove(container);
ClearContainer(container.inventory);
container.Kill();
}
}
}
#endregion
}
#endregion
#region Commands
[ConsoleCommand("hr")]
private void ccmdHR(ConsoleSystem.Arg arg) {
if (arg.Connection != null) return;
if (arg.Args == null || arg.Args.Length == 0) {
Puts("hr call - Call a refueling helicopter");
Puts("hr killall - Kill all refueling helicopters");
Puts("hr forcerf - Force all active helicopters to refuel");
Puts("hr cancelrf - Force all active helicopters to finish refuelling");
return;
}
switch (arg.Args[0].ToLower()) {
case "call" : SpawnEntity();
return;
case "killall" : Puts($"Killing {RefuelHelicopter.allHelicopters.Count} refuel helicopters");
for (int i = 0; i < RefuelHelicopter.allHelicopters.Count; i++) {
RefuelHelicopter refuelHelicopter = RefuelHelicopter.allHelicopters[i];
refuelHelicopter.killSilent = true;
UnityEngine.Object.Destroy(refuelHelicopter);
}
return;
case "forcerf" : for (int i = 0; i < RefuelHelicopter.allHelicopters.Count; i++) {
RefuelHelicopter refuelHelicopter = RefuelHelicopter.allHelicopters[i];
if (!refuelHelicopter.hasValidLocation) {
InvokeHandler.CancelInvoke(refuelHelicopter, refuelHelicopter.TriggerRefuel);
// ... [Code preview truncated due to length. Full source in attached file] ...