C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Ext.ChaosNPC;
using Rust;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("PilotEject", "k1lly0u", "3.1.13")]
[Description("A mini event where a helicopter malfunctions and the pilot has to eject")]
class PilotEject : RustPlugin, IChaosNPCPlugin
{
#region Fields
[PluginReference] Plugin HeliRefuel;
private readonly List<CustomScientistNPC> customNpcs = new List<CustomScientistNPC>();
public static PilotEject Instance { get; private set; }
private const string ADMIN_PERM = "piloteject.admin";
private const string HELICOPTER_PREFAB = "assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab";
private const string SMOKE_EFFECT = "assets/bundled/prefabs/fx/smoke_signal_full.prefab";
private const int LAND_LAYERS = 1 << 4 | 1 << 8 | 1 << 16 | 1 << 21 | 1 << 23;
private static FieldInfo SetReachedSpinoutLocation;
private static MethodInfo StartSpinout;
#endregion
#region Oxide Hooks
private void Loaded()
{
Instance = this;
lang.RegisterMessages(Messages, this);
permission.RegisterPermission(ADMIN_PERM, this);
SetReachedSpinoutLocation = typeof(PatrolHelicopterAI).GetField("reachedSpinoutLocation", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
StartSpinout = typeof(PatrolHelicopterAI).GetMethod("StartSpinout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
}
private void OnServerInitialized()
{
if (configData.Automation.AutoSpawn)
RunAutomatedEvent();
}
private void OnEntitySpawned(PatrolHelicopter baseHelicopter)
{
timer.In(1f, () =>
{
if (baseHelicopter == null || baseHelicopter.GetComponent<EjectionComponent>())
return;
if (HeliRefuel && (bool)HeliRefuel.Call("IsRefuelHelicopter", baseHelicopter))
return;
if (Core.Interface.Call("CanConvertToPilotEject", baseHelicopter) != null)
return;
if (Mathf.Approximately(configData.Automation.Chance, 100) || Random.Range(0, 100) < configData.Automation.Chance)
baseHelicopter.gameObject.AddComponent<EjectionComponent>();
});
}
private void OnEntityTakeDamage(PatrolHelicopter baseHelicopter, HitInfo hitInfo)
{
if (!baseHelicopter || hitInfo == null)
return;
EjectionComponent refuelHelicopter = baseHelicopter.GetComponent<EjectionComponent>();
if (refuelHelicopter != null)
refuelHelicopter.OnTakeDamage(hitInfo);
}
private void OnEntityKill(PatrolHelicopter baseHelicopter)
{
if (baseHelicopter == null)
return;
EjectionComponent ejectionHelictoper = baseHelicopter.GetComponent<EjectionComponent>();
if (ejectionHelictoper != null)
{
if (!ejectionHelictoper.hasEjected)
{
if (configData.Ejection.EjectOnDeath)
ejectionHelictoper.EjectPilot();
return;
}
UnityEngine.Object.Destroy(ejectionHelictoper);
}
for (int i = 0; i < ParachuteControl._allParachutes.Count; i++)
{
ParachuteControl parachutePhysics = ParachuteControl._allParachutes[i];
if (parachutePhysics != null && parachutePhysics.helicopter == baseHelicopter)
parachutePhysics.crashSite = baseHelicopter.transform.position;
}
}
private void OnEntityKill(CustomScientistNPC customNpc) => customNpcs.Remove(customNpc);
private void OnHelicopterRetire(PatrolHelicopterAI patrolHelicopterAI) => UnityEngine.Object.Destroy(patrolHelicopterAI?.helicopterBase?.GetComponent<EjectionComponent>());
private object CanBeTargeted(BaseCombatEntity player, MonoBehaviour behaviour)
{
ParachuteControl npcParachute = player?.GetComponent<ParachuteControl>();
if (npcParachute != null)
{
if ((behaviour is AutoTurret or GunTrap or FlameTurret) && configData.NPC.TargetedByTurrets)
return null;
return false;
}
return null;
}
private void OnEntityDeath(CustomScientistNPC customNpc, HitInfo hitInfo)
{
if (customNpc == null || !customNpcs.Contains(customNpc))
return;
if (configData.Notifications.NPCDeath)
{
if (hitInfo != null && hitInfo.InitiatorPlayer != null)
Broadcast("Notification.PilotKilled", hitInfo.InitiatorPlayer.displayName);
else Broadcast("Notification.PilotKilled2");
}
}
private void Unload()
{
for (int i = EjectionComponent.allHelicopters.Count - 1; i >= 0; i--)
UnityEngine.Object.Destroy(EjectionComponent.allHelicopters[i]);
for (int i = ParachuteControl._allParachutes.Count - 1; i >= 0; i--)
UnityEngine.Object.Destroy(ParachuteControl._allParachutes[i]);
configData = null;
Instance = null;
}
#endregion
#region Functions
private void RunAutomatedEvent()
{
timer.In(Random.Range(configData.Automation.Min, configData.Automation.Max), () =>
{
if (BasePlayer.activePlayerList.Count >= configData.Automation.RequiredPlayers)
SpawnEntity();
RunAutomatedEvent();
});
}
private EjectionComponent SpawnEntity()
{
PatrolHelicopter baseHelicopter = GameManager.server.CreateEntity(HELICOPTER_PREFAB) as PatrolHelicopter;
baseHelicopter.enableSaving = false;
baseHelicopter.Spawn();
return baseHelicopter.gameObject.AddComponent<EjectionComponent>();
}
private static void ClearContainer(ItemContainer container)
{
if (container == null || container.itemList == null)
return;
while (container.itemList.Count > 0)
{
Item item = container.itemList[0];
item.RemoveFromContainer();
item.Remove(0f);
}
}
private static void PopulateLoot(ItemContainer container, ConfigData.LootContainer loot)
{
if (container == null || loot == null)
return;
ClearContainer(container);
int amount = Random.Range(loot.Minimum, loot.Maximum);
List<ConfigData.LootItem> list = Pool.Get<List<ConfigData.LootItem>>();
list.AddRange(loot.Items);
int itemCount = 0;
while (itemCount < amount)
{
int totalWeight = list.Sum((ConfigData.LootItem x) => Mathf.Max(1, x.Weight));
int random = Random.Range(0, totalWeight);
for (int i = list.Count - 1; i >= 0; i--)
{
ConfigData.LootItem lootItem = list[i];
totalWeight -= Mathf.Max(1, lootItem.Weight);
if (random >= totalWeight)
{
list.Remove(lootItem);
Item item = ItemManager.CreateByName(lootItem.Name, Random.Range(lootItem.Minimum, lootItem.Maximum), lootItem.Skin);
item?.MoveToContainer(container);
itemCount++;
break;
}
}
if (list.Count == 0)
list.AddRange(loot.Items);
}
Pool.FreeUnmanaged(ref list);
}
#endregion
#region CustomNPC
public bool InitializeStates(BaseAIBrain customNPCBrain) => false;
public bool WantsToPopulateLoot(CustomScientistNPC customNpc, NPCPlayerCorpse npcplayerCorpse)
{
if (configData.NPC.UseCustomLootTable)
{
PopulateLoot(npcplayerCorpse.containers[0], configData.NPC.RandomItems);
return true;
}
return false;
}
public byte[] GetCustomDesign() => null;
#endregion
#region Component
private class EjectionComponent : MonoBehaviour
{
internal static List<EjectionComponent> allHelicopters = new List<EjectionComponent>();
internal PatrolHelicopter Helicopter { get; private set; }
internal PatrolHelicopterAI AI { get; private set; }
private float actualHealth;
internal bool ejectOverride = false;
internal bool hasEjected = false;
private void Awake()
{
allHelicopters.Add(this);
Helicopter = GetComponent<PatrolHelicopter>();
AI = Helicopter.myAI;
if (configData.Helicopter.DamageEffects)
{
PatrolHelicopter.weakspot weakspot = Helicopter.weakspots[1];
weakspot.healthFractionOnDestroyed = 0f;
weakspot.Hurt(weakspot.health, null);
}
actualHealth = Helicopter.health = configData.Helicopter.Health;
Helicopter.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
if (configData.Ejection.EjectRandom)
InvokeHandler.Invoke(this, TryAutoEjection, Random.Range(configData.Ejection.Min, configData.Ejection.Max));
}
private void OnDestroy() => allHelicopters.Remove(this);
internal void OnTakeDamage(HitInfo hitInfo)
{
if (!configData.Ejection.EjectOnKilled || hasEjected)
return;
actualHealth -= hitInfo.damageTypes.Total();
if (actualHealth < 0f)
EjectPilot();
}
private void TryAutoEjection()
{
if (TerrainMeta.HeightMap.GetHeight(transform.position) < 0 || BasePlayer.activePlayerList.Count < configData.Automation.RequiredPlayers)
{
InvokeHandler.Invoke(this, TryAutoEjection, Random.Range(30, 60));
return;
}
EjectPilot();
}
internal void EjectPilot()
{
if ((BasePlayer.activePlayerList.Count < configData.Automation.RequiredPlayers && !ejectOverride) || hasEjected)
{
Destroy(this);
return;
}
InvokeHandler.CancelInvoke(this, EjectPilot);
ServerMgr.Instance.StartCoroutine(SpawnNPCs(Helicopter));
DropLoot();
hasEjected = true;
if (actualHealth < 0f)
{
if (configData.Notifications.Death)
Broadcast("Notification.OnDeath", MapHelper.PositionToString(transform.position));
}
else
{
if (configData.Notifications.Malfunction)
Broadcast("Notification.Malfunction", MapHelper.PositionToString(transform.position));
}
/*bool previous = PatrolHelicopterAI.monument_crash;
PatrolHelicopterAI.monument_crash = false;
ServerMgr.Instance.Invoke(() =>
{
PatrolHelicopterAI.monument_crash = previous;
}, 0.1f);*/
if (!AI.isDead)
{
AI.CriticalDamage();
SetReachedSpinoutLocation.SetValue(AI, true);
StartSpinout.Invoke(AI, null);
}
Destroy(this);
}
private IEnumerator SpawnNPCs(PatrolHelicopter baseHelicopter)
{
Vector3 position = transform.position + (transform.up + (transform.forward * 2f));
Quaternion rotation = transform.rotation;
configData.NPC.CustomNPCSettings.EquipWeapon = false;
configData.NPC.CustomNPCSettings.EnableNavMesh = false;
configData.NPC.CustomNPCSettings.CanUseWeaponMounted = false;
for (int i = 0; i < configData.NPC.Amount; i++)
{
CustomScientistNPC customNpc = ChaosNPC.SpawnNPC(Instance, position, configData.NPC.CustomNPCSettings);
if (customNpc)
{
Item slot = customNpc.inventory.containerWear.GetSlot(7);
if (slot != null)
{
slot.RemoveFromContainer();
slot.Remove();
}
Item item = ItemManager.CreateByName("parachute");
item.position = 7;
item.SetParent(customNpc.inventory.containerWear);
customNpc.ServerRotation = rotation;
customNpc.modelState.flying = true;
Instance.customNpcs.Add(customNpc);
customNpc.gameObject.AddComponent<ParachuteControl>().helicopter = baseHelicopter;
}
yield return CoroutineEx.waitForSeconds(0.5f);
}
}
private void DropLoot()
{
if (configData.LootBoxes.Amount <= 0)
return;
for (int i = 0; i < configData.LootBoxes.Amount; i++)
{
LootContainer container = GameManager.server.CreateEntity(Helicopter.crateToDrop.resourcePath, transform.position) as LootContainer;
container.enableSaving = false;
container.Spawn();
Vector3 velocity = Random.onUnitSphere;
velocity.y = 1;
Rigidbody rb = container.gameObject.AddComponent<Rigidbody>();
rb.useGravity = true;
rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rb.mass = 2f;
rb.interpolation = RigidbodyInterpolation.Interpolate;
rb.angularVelocity = UnityEngine.Vector3Ex.Range(-1.75f, 1.75f);
rb.drag = 0.5f * (rb.mass / 5f);
rb.angularDrag = 0.2f * (rb.mass / 5f);
rb.AddForce(velocity * 5f, ForceMode.Impulse);
ClearContainer(container.inventory);
PopulateLoot(container.inventory, configData.LootBoxes.RandomItems);
}
}
}
internal class ParachuteControl : MonoBehaviour
{
internal static List<ParachuteControl> _allParachutes = new List<ParachuteControl>();
private CustomScientistNPC m_Entity;
private Parachute m_Parachute;
private Transform m_Transform;
private Rigidbody m_Rigidbody;
public PatrolHelicopter helicopter;
public Vector3 crashSite;
private bool isFalling;
private bool wasWounded;
private Vector3 DirectionTowardsCrash2D
{
get
{
if (helicopter && !helicopter.IsDestroyed)
return (helicopter.transform.position.XZ3D() - m_Transform.position.XZ3D()).normalized;
return (crashSite.XZ3D() - m_Transform.position.XZ3D()).normalized;
}
}
private void Awake()
{
m_Entity = GetComponent<CustomScientistNPC>();
m_Entity.SetPaused(true);
m_Transform = m_Entity.Transform;
InitializeVelocity();
_allParachutes.Add(this);
wasWounded = m_Entity.HasPlayerFlag(BasePlayer.PlayerFlags.Wounded);
m_Entity.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, false);
}
private void OnDestroy()
{
_allParachutes.Remove(this);
if (m_Parachute && !m_Parachute.IsDestroyed)
{
m_Parachute.ParachuteCollider.enabled = false;
m_Parachute.enabled = false;
}
}
private void Update()
{
m_Entity.modelState.onground = false;
m_Entity.modelState.flying = true;
if (!isFalling && m_Rigidbody.velocity.y < 0)
{
DeployParachute();
isFalling = true;
}
if (m_Parachute)
{
if (Physics.Raycast(m_Parachute.transform.position, Vector3.down, out RaycastHit raycastHit, 2f, LAND_LAYERS))
{
enabled = false;
isFalling = false;
m_Rigidbody.useGravity = false;
m_Rigidbody.isKinematic = true;
m_Entity.modelState.onground = true;
m_Entity.modelState.flying = false;
m_Entity.mounted.Set(null);
m_Entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
m_Parachute._mounted = null;
m_Parachute.SetFlag(BaseEntity.Flags.Busy, false, false, true);
m_Parachute.Kill(BaseNetworkable.DestroyMode.None);
if (!NavmeshSpawnPoint.Find(raycastHit.point, 15f, out Vector3 position))
{
m_Entity.Die(new HitInfo(null, m_Entity, DamageType.Fall, 1000f));
return;
}
m_Entity.transform.position = position;
m_Entity.transform.rotation = Quaternion.identity;
m_Entity.NavAgent.Warp(position);
if (m_Entity.WaterFactor() > 0.85f)
{
m_Entity.Die(new HitInfo(null, m_Entity, DamageType.Drowned, 1000f));
return;
}
OnParachuteLand();
return;
}
if (isFalling)
{
Vector3 windAtPosition = Vector3.Lerp(DirectionTowardsCrash2D, GetWindAtCurrentPos(), helicopter && !helicopter.IsDestroyed ? 0.25f : 0.75f);
float heightFromGround = Mathf.Max(TerrainMeta.HeightMap.GetHeight(m_Transform.position), TerrainMeta.WaterMap.GetHeight(m_Transform.position));
float force = Mathf.InverseLerp(heightFromGround + 20f, heightFromGround + 60f, m_Transform.position.y);
Vector3 normalizedDir = windAtPosition.normalized * (force * configData.Ejection.Wind);
Vector3 direction = m_Transform.InverseTransformDirection(DirectionTowardsCrash2D + normalizedDir);
UpdatePlayerInput((direction.x < 0.5f ? (int)BUTTON.LEFT : direction.x > 0.5f ? (int)BUTTON.RIGHT : 0) | (int)BUTTON.FORWARD);
m_Parachute.PlayerServerInput(m_Entity.serverInput, m_Entity);
}
}
}
private void UpdatePlayerInput(int buttons)
{
m_Entity.serverInput.previous.aimAngles = m_Entity.serverInput.current.aimAngles;
m_Entity.serverInput.previous.buttons = m_Entity.serverInput.current.buttons;
m_Entity.serverInput.previous.mouseDelta = m_Entity.serverInput.current.mouseDelta;
m_Entity.serverInput.current.aimAngles = Vector3.zero;
m_Entity.serverInput.current.buttons = buttons;
m_Entity.serverInput.current.mouseDelta = Vector3.zero;
}
private void InitializeVelocity()
{
m_Rigidbody = m_Entity.GetComponent<Rigidbody>();
m_Rigidbody.useGravity = true;
m_Rigidbody.isKinematic = false;
m_Rigidbody.AddForce((Vector3.up * Random.Range(10, 20)) + (Random.onUnitSphere.XZ3D() * 10), ForceMode.Impulse);
}
private void DeployParachute()
{
Item slot = m_Entity.inventory.containerWear.GetSlot(7);
if (slot != null && slot.info.TryGetComponent<ItemModParachute>(out ItemModParachute itemModParachute))
{
m_Parachute = GameManager.server.CreateEntity(itemModParachute.ParachuteVehiclePrefab.resourcePath, m_Entity.transform.position, m_Entity.eyes.rotation, true) as Parachute;
if (m_Parachute)
{
Destroy(m_Parachute.GetComponent<EntityCollisionMessage>());
m_Parachute.skinID = slot.skin;
m_Parachute.Spawn();
m_Parachute.HurtAmount = 0;
m_Parachute.AttemptMount(m_Entity, false);
if (m_Entity.isMounted)
{
if (configData.Ejection.ShowSmoke)
Effect.server.Run(SMOKE_EFFECT, m_Parachute, 0, Vector3.zero, Vector3.zero, null, true);
m_Parachute.ParachuteCollider.enabled = false;
m_Parachute.enabled = false;
m_Parachute.ConditionLossPerUse = 1f;
//m_Parachute._health = m_Parachute.ConditionLossPerUse * m_Parachute.MaxHealth();
slot.Remove(0f);
ItemManager.DoRemoves();
m_Entity.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
return;
}
m_Parachute.Kill(BaseNetworkable.DestroyMode.None);
}
}
}
private Vector3 GetWindAtCurrentPos()
{
float single = m_Transform.position.y * 6f;
Vector3 force = new Vector3(Mathf.Sin(single * 0.0174532924f), 0f, Mathf.Cos(single * 0.0174532924f));
return force.normalized * 1f;
}
private void OnParachuteLand()
{
/*if (NavmeshSpawnPoint.Find(m_Entity.transform.position, 10f, out Vector3 position))
m_Entity.transform.position = position;*/
ChaosNPC.SetRoamHomePosition(m_Entity, helicopter && !helicopter.IsDestroyed ? helicopter.transform.position : crashSite);
if (wasWounded)
m_Entity.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, true);
m_Entity.SetPaused(false);
m_Entity.EquipWeapon();
Destroy(this);
}
}
/*internal class ParachutePhysics : MonoBehaviour
{
internal static List<ParachutePhysics> _allParachutes = new List<ParachutePhysics>();
internal CustomScientistNPC CustomNpc { get; private set; }
internal PatrolHelicopter Helicopter { get; set; }
private Transform tr;
private Rigidbody rb;
//private BaseEntity parachute;
private Vector3 currentWindVector = Vector3.zero;
internal Vector3 crashSite;
private bool isFalling;
private bool wasWounded = false;
private Vector3 DirectionTowardsCrash2D
{
get
{
if (Helicopter && !Helicopter.IsDestroyed)
return (Helicopter.transform.position.XZ3D() - tr.position.XZ3D()).normalized;
return (crashSite.XZ3D() - tr.position.XZ3D()).normalized;
}
}
private void Awake()
{
CustomNpc = GetComponent<CustomScientistNPC>();
CustomNpc.SetPaused(true);
tr = CustomNpc.Transform;
InitializeVelocity();
_allParachutes.Add(this);
wasWounded = CustomNpc.HasPlayerFlag(BasePlayer.PlayerFlags.Wounded);
CustomNpc.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, false);
}
private void OnDestroy()
{
_allParachutes.Remove(this);
//if (parachute != null && !parachute.IsDestroyed)
// parachute.Kill();
}
private void Update()
{
CustomNpc.modelState.onground = false;
CustomNpc.modelState.flying = true;
if (!isFalling)
return;
else
{
if (Physics.Raycast(tr.position, Vector3.down, 0.5f, LAND_LAYERS))
{
enabled = false;
isFalling = false;
rb.useGravity = false;
rb.isKinematic = true;
rb.drag = 0;
CustomNpc.modelState.onground = true;
CustomNpc.modelState.flying = false;
//if (parachute != null && !parachute.IsDestroyed)
// parachute.Kill();
if (TerrainMeta.WaterMap.GetDepth(tr.position) > 0.6f)
{
CustomNpc.Die(new HitInfo(null, CustomNpc, DamageType.Drowned, 1000f));
return;
}
OnParachuteLand();
}
}
}
private void FixedUpdate()
{
if (!isFalling && rb.velocity.y < 0)
DeployParachute();
if (isFalling)
{
Vector3 windAtPosition = Vector3.Lerp(DirectionTowardsCrash2D, GetWindAtCurrentPos(), Helicopter != null && !Helicopter.IsDestroyed ? 0.25f : 0.75f);
float heightFromGround = Mathf.Max(TerrainMeta.HeightMap.GetHeight(tr.position), TerrainMeta.WaterMap.GetHeight(tr.position));
float force = Mathf.InverseLerp(heightFromGround + 20f, heightFromGround + 60f, tr.position.y);
Vector3 normalizedDir = (windAtPosition.normalized * force) * configData.Ejection.Wind;
currentWindVector = Vector3.Lerp(currentWindVector, normalizedDir, Time.fixedDeltaTime * 0.25f);
rb.AddForceAtPosition(normalizedDir * 0.1f, tr.position, ForceMode.Force);
rb.AddForce(normalizedDir * 0.9f, ForceMode.Force);
Quaternion rotation = Quaternion.LookRotation(rb.velocity);
tr.rotation = CustomNpc.eyes.rotation = CustomNpc.ServerRotation = rotation;
CustomNpc.viewAngles = rotation.eulerAngles;
/*parachute.transform.localRotation = Quaternion.Euler(0f, rotation.eulerAngles.y, 0f);
parachute.SendNetworkUpdate();#1#
}
}
private void InitializeVelocity()
{
rb = CustomNpc.GetComponent<Rigidbody>();
rb.useGravity = true;
rb.isKinematic = false;
rb.AddForce((Vector3.up * 15) + (Random.onUnitSphere.XZ3D() * 5), ForceMode.Impulse);
}
private void DeployParachute()
{
ItemModParachute itemModParachute;
Item slot = CustomNpc.inventory.containerWear.GetSlot(7);
if (slot != null && slot.info.TryGetComponent<ItemModParachute>(out itemModParachute))
{
Parachute parachute = GameManager.server.CreateEntity(itemModParachute.ParachuteVehiclePrefab.resourcePath, base.transform.position, CustomNpc.eyes.rotation, true) as Parachute;
if (parachute)
{
parachute.skinID = slot.skin;
parachute.Spawn();
parachute.AttemptMount(CustomNpc, false);
if (CustomNpc.isMounted)
{
if (configData.Ejection.ShowSmoke)
Effect.server.Run(SMOKE_EFFECT, parachute, 0, Vector3.zero, Vector3.zero, null, true);
parachute.ParachuteCollider.enabled = false;
slot.Remove(0f);
ItemManager.DoRemoves();
CustomNpc.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
return;
}
parachute.Kill(BaseNetworkable.DestroyMode.None);
}
}
/*parachute = GameManager.server.CreateEntity(PARACHUTE_PREFAB, tr.position);
parachute.enableSaving = false;
parachute.skinID = 0;
parachute.Spawn();
parachute.SetParent(CustomNpc, false);
parachute.transform.localPosition = Vector3.up * 2f;
parachute.transform.localRotation = Quaternion.Euler(0f, CustomNpc.viewAngles.y, 0f);
foreach (BaseEntity.Flags f in (BaseEntity.Flags[])System.Enum.GetValues(typeof(BaseEntity.Flags)))
{
parachute.SetFlag(f, true, true);
}#1#
//parachute.SetFlag(BaseEntity.Flags.Reserved11, true, true);
rb.drag = configData.Ejection.Drag;
/*CustomNpc.Invoke(() =>
{
CustomNpc.modelState.onground = false;
CustomNpc.modelState.flying = true;
CustomNpc.SendNetworkUpdate();
}, 1f);#1#
isFalling = true;
}
private Vector3 GetWindAtCurrentPos()
{
float single = tr.position.y * 6f;
Vector3 force = new Vector3(Mathf.Sin(single * 0.0174532924f), 0f, Mathf.Cos(single * 0.0174532924f));
return force.normalized * 1f;
}
private void OnParachuteLand()
{
ChaosNPC.SetRoamHomePosition(CustomNpc, Helicopter != null && !Helicopter.IsDestroyed ? Helicopter.transform.position : crashSite);
if (wasWounded)
CustomNpc.SetPlayerFlag(BasePlayer.PlayerFlags.Wounded, true);
CustomNpc.SetPaused(false);
CustomNpc.EquipWeapon();
Destroy(this);
}
} */
#endregion
#region Commands
[ChatCommand("pe")]
private void cmdPilotEject(BasePlayer player, string command, string[] args)
{
if (!permission.UserHasPermission(player.UserIDString, ADMIN_PERM) && !player.IsAdmin)
return;
if (args.Length == 0)
{
SendReply(player, "/pe call - Call a PilotEject helicopter");
SendReply(player, "/pe eject - Force eject any active pilots");
return;
}
switch (args[0].ToLower())
{
case "call":
SpawnEntity();
SendReply(player, "Spawned PilotEject helicopter");
return;
case "c":
if (player.net.connection.authLevel == 2)
{
EjectionComponent ejectionComponent = SpawnEntity();
timer.In(1f, () => ejectionComponent.Helicopter.transform.position = player.transform.position);
}
return;
case "t":
if (player.net.connection.authLevel == 2)
{
EjectionComponent ejectionComponent = SpawnEntity();
timer.In(0.75f, () => ejectionComponent.Helicopter.transform.position = player.transform.position);
timer.In(1.5f, () =>
{
ejectionComponent.ejectOverride = true;
ejectionComponent.EjectPilot();
});
}
return;
case "s":
if (player.net.connection.authLevel == 2)
{
for (int i = 0; i < EjectionComponent.allHelicopters.Count; i++)
{
EjectionComponent ejectionComponent = EjectionComponent.allHelicopters[i];
ejectionComponent.Helicopter.Die(new HitInfo(ejectionComponent.Helicopter, ejectionComponent.Helicopter, Rust.DamageType.Explosion, 100000));
}
}
return;
case "eject":
int count = 0;
for (int i = 0; i < EjectionComponent.allHelicopters.Count; i++)
{
EjectionComponent ejectionComponent = EjectionComponent.allHelicopters[i];
if (!ejectionComponent.hasEjected)
{
ejectionComponent.ejectOverride = true;
ejectionComponent.EjectPilot();
count++;
}
}
SendReply(player, $"Ejected {count * configData.NPC.Amount} pilots from {count} helicopters");
return;
default:
SendReply(player, "Invalid syntax!");
break;
}
}
[ConsoleCommand("pe")]
private void ccmdPilotEject(ConsoleSystem.Arg arg)
{
if (arg.Connection != null)
{
if (!permission.UserHasPermission(arg.Connection.userid.ToString(), ADMIN_PERM))
{
SendReply(arg, "You do not have permission to use this command");
return;
}
}
if (arg.Args.Length == 0)
{
SendReply(arg, "pe call - Call a PilotEject helicopter");
SendReply(arg, "pe eject - Force eject any active pilots");
return;
}
switch (arg.Args[0].ToLower())
{
case "call":
SpawnEntity();
return;
case "eject":
int count = 0;
for (int i = 0; i < EjectionComponent.allHelicopters.Count; i++)
{
EjectionComponent ejectionComponent = EjectionComponent.allHelicopters[i];
if (!ejectionComponent.hasEjected)
{
ejectionComponent.ejectOverride = true;
ejectionComponent.EjectPilot();
count++;
}
}
SendReply(arg, $"Ejected {count * configData.NPC.Amount} pilots from {count} helicopters");
return;
default:
SendReply(arg, "Invalid syntax!");
break;
}
}
#endregion
#region Config
private static ConfigData configData;
private class ConfigData
{
[JsonProperty(PropertyName = "Event Automation")]
public AutomationOptions Automation { get; set; }
[JsonProperty(PropertyName = "Ejection Options")]
public EjectionOptions Ejection { get; set; }
[JsonProperty(PropertyName = "Helicopter Options")]
public HelicopterOptions Helicopter { get; set; }
[JsonProperty(PropertyName = "Notification Options")]
public NotificationOptions Notifications { get; set; }
[JsonProperty(PropertyName = "NPC Options")]
public NPCOptions NPC { get; set; }
[JsonProperty(PropertyName = "Loot Container Options")]
public Loot LootBoxes { get; set; }
public class AutomationOptions
{
[JsonProperty(PropertyName = "Automatically spawn helicopters on a timer")]
public bool AutoSpawn { get; set; }
[JsonProperty(PropertyName = "Auto-spawn time minimum (seconds)")]
public float Min { get; set; }
[JsonProperty(PropertyName = "Auto-spawn time maximum (seconds)")]
public float Max { get; set; }
[JsonProperty(PropertyName = "Minimum amount of online players to trigger the event")]
public int RequiredPlayers { get; set; }
[JsonProperty(PropertyName = "Chance of game spawned helicopter becoming a PilotEject helicopter (x / 100)")]
public float Chance { get; set; }
}
public class EjectionOptions
{
[JsonProperty(PropertyName = "Eject the pilot when the helicopter has been shot down")]
public bool EjectOnKilled { get; set; }
[JsonProperty(PropertyName = "Eject the pilot when the helicopter has been destroyed mid-air")]
public bool EjectOnDeath { get; set; }
[JsonProperty(PropertyName = "Eject the pilot randomly")]
public bool EjectRandom { get; set; }
[JsonProperty(PropertyName = "Show smoke when parachuting")]
public bool ShowSmoke { get; set; }
[JsonProperty(PropertyName = "Random ejection time minimum (seconds)")]
public float Min { get; set; }
[JsonProperty(PropertyName = "Random ejection time maximum (seconds)")]
public float Max { get; set; }
[JsonProperty(PropertyName = "Parachute drag force")]
public float Drag { get; set; }
[JsonProperty(PropertyName = "Wind force")]
public float Wind { get; set; }
}
public class HelicopterOptions
{
[JsonProperty(PropertyName = "Helicopter spawns with tail rotor on fire")]
public bool DamageEffects { get; set; }
[JsonProperty(PropertyName = "Start health")]
public float Health { get; set; }
}
public class NotificationOptions
{
[JsonProperty(PropertyName = "Show notification when helicopter has been shot down")]
public bool Death { get; set; }
[JsonProperty(PropertyName = "Show notification when helicopter malfunctions")]
public bool Malfunction { get; set; }
[JsonProperty(PropertyName = "Show notification when a NPC has been killed")]
public bool NPCDeath { get; set; }
}
public class NPCOptions
{
[JsonProperty(PropertyName = "Amount of NPCs to spawn")]
public int Amount { get; set; }
[JsonProperty(PropertyName = "Custom NPC Settings")]
public NPCSettings CustomNPCSettings { get; set; }
[JsonProperty(PropertyName = "Use custom loot table")]
public bool UseCustomLootTable { get; set; }
[JsonProperty(PropertyName = "Random loot items")]
public LootContainer RandomItems { get; set; }
[JsonProperty(PropertyName = "Can be targeted by turrets")]
public bool TargetedByTurrets { get; set; }
}
public class Loot
{
[JsonProperty(PropertyName = "Amount of loot boxes to drop when pilot ejects")]
public int Amount { get; set; }
[JsonProperty(PropertyName = "Loot container items")]
public LootContainer RandomItems { get; set; }
}
public class LootContainer
{
[JsonProperty(PropertyName = "Minimum amount of items")]
public int Minimum { get; set; }
[JsonProperty(PropertyName = "Maximum amount of items")]
public int Maximum { get; set; }
[JsonProperty(PropertyName = "Items")]
public LootItem[] Items { get; set; }
}
public class LootItem
{
[JsonProperty(PropertyName = "Item shortname")]
public string Name { get; set; }
[JsonProperty(PropertyName = "Item skin ID")]
public ulong Skin { get; set; }
[JsonProperty(PropertyName = "Minimum amount of item")]
public int Minimum { get; set; }
[JsonProperty(PropertyName = "Maximum amount of item")]
public int Maximum { get; set; }
[JsonProperty(PropertyName = "Item weight (a larger number has more chance of being selected)")]
public int Weight { get; set; } = 1;
}
public Oxide.Core.VersionNumber Version { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
configData = Config.ReadObject<ConfigData>();
if (configData.Version < Version)
UpdateConfigValues();
Config.WriteObject(configData, true);
}
protected override void LoadDefaultConfig() => configData = GetBaseConfig();
private ConfigData GetBaseConfig()
{
return new ConfigData
{
Automation = new ConfigData.AutomationOptions
{
AutoSpawn = false,
Chance = 100,
Min = 3600,
Max = 5400,
RequiredPlayers = 1,
},
Ejection = new ConfigData.EjectionOptions
{
Drag = 2f,
EjectOnDeath = false,
EjectOnKilled = true,
EjectRandom = false,
ShowSmoke = false,
Min = 300,
Max = 600,
Wind = 10f
},
Helicopter = new ConfigData.HelicopterOptions
{
DamageEffects = false,
Health = 10000
},
Notifications = new ConfigData.NotificationOptions
{
Death = true,
Malfunction = true,
NPCDeath = true
},
NPC = new ConfigData.NPCOptions
{
Amount = 2,
RandomItems = new ConfigData.LootContainer
{
Minimum = 3,
Maximum = 5,
Items = new ConfigData.LootItem[]
{
new ConfigData.LootItem {Name = "apple", Skin = 0, Maximum = 6, Minimum = 2 },
new ConfigData.LootItem {Name = "bearmeat.cooked", Skin = 0, Maximum = 4, Minimum = 2 },
new ConfigData.LootItem {Name = "blueberries", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "corn", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "fish.raw", Skin = 0, Maximum = 4, Minimum = 2 },
new ConfigData.LootItem {Name = "granolabar", Skin = 0, Maximum = 4, Minimum = 1 },
new ConfigData.LootItem {Name = "meat.pork.cooked", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "candycane", Skin = 0, Maximum = 2, Minimum = 1 }
}
},
CustomNPCSettings = new NPCSettings
{
WoundedRecoveryChance = 80,
WoundedChance = 15,
RoamRange = 30,
ChaseRange = 90
}
},
LootBoxes = new ConfigData.Loot
{
Amount = 2,
RandomItems = new ConfigData.LootContainer
{
Minimum = 3,
Maximum = 5,
Items = new ConfigData.LootItem[]
{
new ConfigData.LootItem {Name = "apple", Skin = 0, Maximum = 6, Minimum = 2 },
new ConfigData.LootItem {Name = "bearmeat.cooked", Skin = 0, Maximum = 4, Minimum = 2 },
new ConfigData.LootItem {Name = "blueberries", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "corn", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "fish.raw", Skin = 0, Maximum = 4, Minimum = 2 },
new ConfigData.LootItem {Name = "granolabar", Skin = 0, Maximum = 4, Minimum = 1 },
new ConfigData.LootItem {Name = "meat.pork.cooked", Skin = 0, Maximum = 8, Minimum = 4 },
new ConfigData.LootItem {Name = "candycane", Skin = 0, Maximum = 2, Minimum = 1 }
}
}
},
Version = Version
};
}
protected override void SaveConfig() => Config.WriteObject(configData, true);
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...")
// ... [Code preview truncated due to length. Full source in attached file] ...