C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using UnityEngine;
using UnityEngine.AI;
using VLB;
using Newtonsoft.Json;
using UnityEngine.SceneManagement;
using Rust;
using Rust.Ai;
using Facepunch;
// Rewrote most of the code
// GetComponent is expensive, store your components in a dictionary
// Added proper static cleanup and usage
// Proper subscription of hooks
// Fixed server not saving
// Fixed OnEntitySpawned cleanup
// Fixed bradley not spawning due to invalid hackable crate prefab
// Removed reflection as it's not allowed - this will make npcs act a bit differently
// Fixed config not loading or saving
// Changed AI to Apex
// Fixed language API usage
// Removed useless and unused code
namespace Oxide.Plugins {
[Info("Paratroopers", "Skuli Dropek", "4.0.0")]
[Description("Spawns Paratroopers Event that deploys from the Cargo Plane to random area")]
class Paratroopers : RustPlugin {
[PluginReference]
Plugin Kits, Titan;
private static Paratroopers plugin;
#region Vars
private static Paratroopers ins {
get;
set;
}
private StoredData storedData;
private DynamicConfigFile data;
private const string ADMIN_PERM = "paratroopers.admin";
private const string PLAYER_PERM = "paratroopers.cancall";
private NavMeshHit navHit;
private RaycastHit rayHit;
public List<int> FltNum = new List<int>();
private const uint CARGOPLANE_PREFABID = 2383782438;
private const uint NPCPlayer_PREFABID = 4199494415;
//
private const uint CORPSE_PREFABID = 2604534927;
private const uint CHUTE_PREFABID = 1268659691;
private const uint SUPPLYDROP_PREFABID = 3632568684;
private const uint HACKABLECRATE_PREFABID = 209286362;
private const uint BRADLEY_PREFABID = 1456850188;
private const uint SMOKE_EFFECTID = 2278592883;
private const uint SIRENLIGHT_EFFECTID = 2436926577;
private const uint SIRENALARM_EFFECTID = 1056621402;
private const uint SUPPLYDROP_EFFECTID = 135533567;
private LootContainer crate;
const object Null = null;
public bool IsNight;
private List<MapMarkerGenericRadius> dropZoneMarkerList = new List<MapMarkerGenericRadius>();
private Dictionary<ScientistNPC, MapMarkerGenericRadius> botMarkerList = new Dictionary<ScientistNPC, MapMarkerGenericRadius>();
private Dictionary<ScientistNPC, NPCParatrooper> paratrooperList = new Dictionary<ScientistNPC, NPCParatrooper>();
private List<ScientistNPC> jumperList = new List<ScientistNPC>();
private List<ulong> paratrooperCorpseIDs = new List<ulong>();
public List<ulong> DeadNPCPlayerIds = new List<ulong>();
//to tracebackpacks
private Dictionary<CargoPlane, ParatrooperPlane> planes = new Dictionary<CargoPlane, ParatrooperPlane>();
List<LootContainer> drops = new List<LootContainer>();
List<StorageContainer> dropsCrate = new List<StorageContainer>();
List<BradleyAPC> dropBradley = new List<BradleyAPC>();
private const int LAND_LAYERS = 1 << 4 | 1 << 8 | 1 << 16 | 1 << 21 | 1 << 23;
private const int WORLD_LAYER = (1 << 0 | 1 << 16) | ~(1 << 18 | 1 << 28 | 1 << 29);
public const string ChatIcon = "76561198035100747";
#endregion
#region Get Misc Functions
private Vector3 GetDropPosition() {
return new Vector3(configData.Automation.JumpPositionX, configData.Automation.jumpHeight, configData.Automation.JumpPositionZ);
}
public int GetDefaultJumpSpread() {
return configData.Paratroopers.defaultJumpSpread;
}
public float GetJumpHeight() {
return configData.Automation.jumpHeight;
}
public string GetInboundMessage() {
return msg("InboundJumpPositionMessageChat");
}
public float GetPlaneSpeed() {
return configData.Automation.PlaneSpeed;
}
#endregion
#region startup/close/clean-up
private void OnServerInitialized() {
//Start Loaded
Unsubscribe();
IsNight = TOD_Sky.Instance.IsNight;
if (configData.Notifications.ConsoleOutput) {
if (Kits == null) {
Puts("WARNING: Kits Plugin is required");
}
else Puts("Found dependency Kits");
}
ins = this;
lang.RegisterMessages(Messages, this);
permission.RegisterPermission(ADMIN_PERM, this);
permission.RegisterPermission(PLAYER_PERM, this);
foreach (var kvp in configData.Cooldowns) {
if (!kvp.Key.StartsWith("paratroopers.")) {
permission.RegisterPermission("paratroopers." + kvp.Key, this);
}
else permission.RegisterPermission(kvp.Key, this);
}
if (configData.MapSettings.CreateBotMarkers) {
timer.Repeat(configData.MapSettings.BotMarkerRefreshInterval, 0, () => RefreshBotMarkers());
}
//End Loaded
ins = this;
LoadData();
if (configData.Automation.AutoSpawn == true) {
float nextJump = Oxide.Core.Random.Range(configData.Automation.RandomJumpIntervalMinSec, configData.Automation.RandomJumpIntervalMaxSec);
timer.Once(nextJump, () => RepeatRandomJump());
if (configData.Notifications.ConsoleOutput) {
Puts("Random Paratroopers Events will occur in {0}s", nextJump);
}
}
else if (configData.Notifications.ConsoleOutput) {
Puts("Random interval Paratroopers Events are turned off");
}
Subscribe();
}
private void Unload() {
if (configData.Notifications.ConsoleOutput) {
Puts("Save files, destroy timers, etc");
}
WipeTroopers();
WipeMarkers(botMarkerList.Values.ToList());
WipeMarkers(dropZoneMarkerList.ToList());
ins = null;
KillEvent();
configData = null;
SaveData();
}
public void GetFlightNumber() {
FlightNumber = Core.Random.Range(3000, 9999);
FltNum.Add(FlightNumber);
}
private string FormatTime(double time) {
TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
var days = dateDifference.Days;
var hours = dateDifference.Hours;
hours += (days * 24);
var mins = dateDifference.Minutes;
var secs = dateDifference.Seconds;
return string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
}
#endregion
#region hooks
private object OnDeathNotice(Dictionary<string, object> data, string message) // this shouldn't be required ever
{
//PrintWarning(data["VictimEntity"].ToString());
var entity = data["VictimEntity"] as ScientistNPC;
if (entity == null) {
return null;
}
if (paratrooperList.ContainsKey(entity)) {
paratrooperList.Remove(entity);
return false;
}
return null;
}
private object OnCustomLootNPC(uint ID) {
foreach (var npc in paratrooperList.Keys) {
if (npc?.net?.ID == ID) {
if (configData.npc.LootType.ToLower() == "default") {
return null;
}
return true;
}
}
return null;
}
private object OnNpcResume(ScientistNPC npc) {
if (npc == null) {
return null;
}
NPCParatrooper paratrooper;
if (!paratrooperList.TryGetValue(npc, out paratrooper) || paratrooper.hasLanded) {
return null;
}
return true;
}
private void OnEntityKill(SupplyDrop drop) => drops.Remove(drop);
private void OnEntityKill(HackableLockedCrate crate) => dropsCrate.Remove(crate);
private void OnEntityKill(CargoPlane cp) {
if (cp == null) {
return;
}
ParatrooperPlane crashComponent;
if (!planes.TryGetValue(cp, out crashComponent)) {
return;
}
UnityEngine.Object.Destroy(crashComponent);
planes.Remove(cp);
}
private void OnEntityKill(ScientistNPC victim) => OnPlayerDeath(victim, null);
private void OnPlayerDeath(ScientistNPC victim, HitInfo hitInfo) {
if (victim == null) {
return;
}
NPCParatrooper paratrooper;
if (!paratrooperList.TryGetValue(victim, out paratrooper)) {
return;
}
victim.UpdateActiveItem(0);
if (configData.npc.LootType.ToLower() == "inventory") {
paratrooper.PrepareInventory();
}
if (hitInfo == null) {
return;
}
var attacker = hitInfo.Initiator as BasePlayer;
if (attacker == null) {
return;
}
MapMarkerGenericRadius mapMarker;
if (botMarkerList.TryGetValue(victim, out mapMarker)) {
mapMarker.Kill();
mapMarker.SendUpdate();
botMarkerList.Remove(victim);
}
paratrooperCorpseIDs.Add(victim.userID);
if (victim == attacker) {
if (configData.Notifications.NPCSuicide) {
string key = hitInfo.damageTypes.Has(Rust.DamageType.Drowned) ? "ParatrooperDrowned" : "ParatrooperKilledSelf";
foreach (var target in BasePlayer.activePlayerList) {
target.SendConsoleCommand("chat.add", new object[] {
2, ChatIcon, string.Format(msg(key, target.UserIDString), victim.displayName)
}
);
}
}
}
else if (configData.Notifications.NPCDeath) {
foreach (var target in BasePlayer.activePlayerList) {
target.SendConsoleCommand("chat.add", new object[] {
2, ChatIcon, string.Format(msg("ParatrooperKilled", target.UserIDString), attacker.displayName, victim.displayName)
}
);
}
}
}
private void OnEntityDeath(BradleyAPC victim, HitInfo hitInfo) {
if (victim == null || hitInfo == null || !dropBradley.Contains(victim)) {
return;
}
dropBradley.Remove(victim);
victim.GetOrAddComponent<APCController>();
if (!configData.Notifications.APCDeath) {
return;
}
var attacker = hitInfo.Initiator as BasePlayer;
if (attacker == null) {
return;
}
foreach (var target in BasePlayer.activePlayerList) {
target.SendConsoleCommand("chat.add", new object[] {
2, ChatIcon, string.Format(msg("BradleyKilled", target.UserIDString), attacker.displayName)
}
);
}
}
private object OnTurretTarget(NPCAutoTurret turret, ScientistNPC ScientistNPC) => HookHandler(ScientistNPC);
private object CanHelicopterTarget(PatrolHelicopterAI heliAi, ScientistNPC ScientistNPC) => HookHandler(ScientistNPC);
private object CanHelicopterStrafeTarget(PatrolHelicopterAI heliAi, ScientistNPC ScientistNPC) => HookHandler(ScientistNPC);
private object OnNpcTarget(BaseNpc npc, ScientistNPC ScientistNPC) => HookHandler(ScientistNPC);
private object HookHandler(ScientistNPC ScientistNPC) => ScientistNPC != null && paratrooperList.ContainsKey(ScientistNPC) ? true : (object)null;
private object CanBradleyApcTarget(BradleyAPC apc, BasePlayer player) {
if (player == null || apc == null || !dropBradley.Contains(apc)) {
return null;
}
if (player is ScientistNPC) {
return HookHandler(player as ScientistNPC);
}
return player.IsNearEnemyBase() || CanIgnoreSleeper(player) ? false : (object)null;
}
private bool EventTerritory(Vector3 target) {
foreach (var drop in drops) {
if ((drop.transform.position - target).magnitude <= configData.Distance) {
return true;
}
}
foreach (var drop in dropsCrate) {
if ((drop.transform.position - target).magnitude <= configData.Distance) {
return true;
}
}
foreach (var drop in dropBradley) {
if ((drop.transform.position - target).magnitude <= configData.Distance) {
return true;
}
}
return false;
}
private object CanEntityTakeDamage(BasePlayer victim, HitInfo hitInfo) {
if (hitInfo == null || !victim.IsValid() || victim.IsNpc) {
return null;
}
var attacker = hitInfo.Initiator as BasePlayer;
if (!attacker.IsValid()) {
return null;
}
if (EventTerritory(victim.transform.position) && EventTerritory(attacker.transform.position)) {
return true;
}
return null;
}
private void OnEntityTakeDamage(BradleyAPC apc, HitInfo hitInfo) {
if (apc == null || !dropBradley.Contains(apc) || hitInfo == null || !(hitInfo.Initiator is BasePlayer)) {
return;
}
var attacker = hitInfo.Initiator as BasePlayer;
if (!attacker.IsNearEnemyBase()) {
return;
}
hitInfo.damageTypes = new DamageTypeList();
}
private void OnEntityTakeDamage(ScientistNPC ScientistNPC, HitInfo hitInfo) {
if (ScientistNPC == null || hitInfo == null || !(hitInfo.Initiator is BasePlayer)) {
return;
}
NPCParatrooper paratrooper;
if (!paratrooperList.TryGetValue(ScientistNPC, out paratrooper)) {
return;
}
paratrooper.OnReceivedDamage(hitInfo.Initiator as BasePlayer, hitInfo);
}
#region IgnoreSleeper
private object OnNpcTarget(BaseEntity npc, BasePlayer player) => CanIgnoreSleeper(player) ? true : (object)null;
private bool CanIgnoreSleeper(BasePlayer player) {
if (player == null || player is TunnelDweller || !player.userID.IsSteamId() || !player.IsSleeping()) return false;
return true;
}
#endregion IgnoreSleeper
#region WeaponHandling
private int GetRange(float distance) {
if (distance < 2f) return 1;
if (distance < 10f) return 2;
if (distance < 40f) return 3;
return 4;
}
#endregion
private object CanPopulateLoot(ScientistNPC ScientistNPC, NPCPlayerCorpse corpse) => HookHandler(ScientistNPC);
private object OnCorpsePopulate(HumanNPC ScientistNPC, NPCPlayerCorpse corpse) {
if (corpse != null && paratrooperCorpseIDs.Contains(corpse.playerSteamID)) {
paratrooperCorpseIDs.Remove(corpse.playerSteamID);
DeadNPCPlayerIds.Add(corpse.playerSteamID);
if (corpse.containers != null) {
for (int i = 0; i < corpse.containers.Length; i++) {
ClearContainer(corpse.containers[i]);
}
}
}
if (ScientistNPC == null) {
return null;
}
NPCParatrooper paratrooper;
if (!paratrooperList.TryGetValue(ScientistNPC as ScientistNPC, out paratrooper)) return null;
paratrooperList.Remove(ScientistNPC as ScientistNPC);
if (configData.npc.LootType.ToLower() == "inventory") {
paratrooper.MoveInventoryTo(corpse);
return corpse;
}
//if (configData.npc.RandomItems.Maximum > 0)
//{
// PopulateLoot(corpse.containers[0], configData.npc.RandomItems);
//}
return corpse;
}
private object OnEntityDismounted(BaseMountable mount, BasePlayer player) {
if (player.IsNpc && paratrooperList.ContainsKey(player.GetComponent<ScientistNPC>()) && mount.VehicleParent() is CH47Helicopter) {
player.Invoke(() => {
if (player == null) {
return;
}
BaseNavigator BN = player.GetComponent<BaseNavigator>();
if (BN == null) {
return;
}
NavMeshHit hit;
if (NavMesh.SamplePosition(player.transform.position, out hit, 30, -1)) {
player.gameObject.layer = 17;
player.ServerPosition = hit.position;
BN.Agent.Warp(player.ServerPosition);
player.Invoke(() => BN.CanUseNavMesh = true, 2f);
}
}
, 1f);
}
return null;
}
private void Subscribe() // removed all init checks
{
if (configData.Distance > 0 && configData.LootBoxes.LootEffects.dropBradley) {
Subscribe(nameof(CanEntityTakeDamage));
}
Subscribe(nameof(OnEntityKill));
Subscribe(nameof(OnPlayerDeath));
Subscribe(nameof(OnEntityDeath));
Subscribe(nameof(OnEntityTakeDamage));
Subscribe(nameof(OnEntitySpawned));
Subscribe(nameof(CanLootPlayer));
Subscribe(nameof(OnPlayerAssist));
if (configData.npc.LootType.ToLower() != "default") {
Subscribe(nameof(OnCorpsePopulate));
Subscribe(nameof(CanPopulateLoot));
}
}
private void Unsubscribe() {
Unsubscribe(nameof(CanEntityTakeDamage));
Unsubscribe(nameof(OnEntityKill));
Unsubscribe(nameof(OnPlayerDeath));
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnEntityTakeDamage));
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(CanLootPlayer));
Unsubscribe(nameof(OnPlayerAssist));
Unsubscribe(nameof(OnCorpsePopulate));
Unsubscribe(nameof(CanPopulateLoot));
Unsubscribe(nameof(CanBradleyApcTarget));
}
private void OnEntitySpawned(DroppedItemContainer container) {
NextTick(() => {
if (container == null || !DeadNPCPlayerIds.Contains(container.playerSteamID)) return;
DeadNPCPlayerIds.Remove(container.playerSteamID);
if (!container.IsDestroyed) container.Kill();
}
);
}
private object CanLootPlayer(ScientistNPC ScientistNPC, BasePlayer player) {
if (ScientistNPC != null && paratrooperList.ContainsKey(ScientistNPC) && ScientistNPC.IsWounded()) {
return false;
}
return null;
}
private object OnPlayerAssist(ScientistNPC ScientistNPC, BasePlayer player) => HookHandler(ScientistNPC);
private static void RunEffect(string name, BaseEntity entity = null, Vector3 position = new Vector3(), Vector3 offset = new Vector3()) {
if (entity != null) {
Effect.server.Run(name, entity, 0, offset, position, null, true);
}
else Effect.server.Run(name, position, Vector3.up, null, true);
}
private string GetGridString(Vector3 position) {
var adjPosition = new Vector2((World.Size / 2) + position.x, (World.Size / 2) - position.z);
return $"{NumberToString((int)(adjPosition.x / 145))}{((int)(adjPosition.y / 145))}";
}
private static string NumberToString(int number) {
var a = number > 26;
var c = (System.Char)(65 + (a ? number - 26 : number));
return a ? "A" + c : c.ToString();
}
private static void StripInventory(BasePlayer player) {
ClearContainer(player.inventory.containerBelt);
ClearContainer(player.inventory.containerMain);
ClearContainer(player.inventory.containerWear);
}
private static void ClearContainer(ItemContainer container) {
while (container.itemList.Count > 0) {
Item item = container.itemList[0];
item.RemoveFromContainer();
item.Remove();
}
}
private static void PopulateLoot(ItemContainer container, ConfigData.LootContainer loot) {
ClearContainer(container);
var amount = UnityEngine.Random.Range(loot.Minimum, loot.Maximum);
var list = new List<ConfigData.LootItem>(loot.Items);
for (int i = 0; i < amount; i++) {
if (list.Count == 0) {
list = new List<ConfigData.LootItem>(loot.Items);
}
var lootItem = list[UnityEngine.Random.Range(0, list.Count())];
list.Remove(lootItem);
Item item = ItemManager.CreateByName(lootItem.Name, UnityEngine.Random.Range(lootItem.Minimum, lootItem.Maximum), lootItem.Skin);
if (!item.MoveToContainer(container)) item.Remove();
}
}
private static object FindPointOnNavmesh(Vector3 targetPosition) {
targetPosition.y = TerrainMeta.HeightMap.GetHeight(targetPosition);
for (int i = 0; i < 10; i++) {
NavMeshHit navHit;
if (NavMesh.SamplePosition(targetPosition, out navHit, 40, -1)) {
if (IsInOrOnRock(navHit.position)) {
continue;
}
return navHit.position;
}
}
return null;
}
private static bool IsInOrOnRock(Vector3 position, string meshName = "rock_", float radius = 2f) {
bool flag = false;
int hits = Physics.OverlapSphereNonAlloc(position, radius, Vis.colBuffer, Layers.Mask.World, QueryTriggerInteraction.Ignore);
for (int i = 0; i < hits; i++) {
if (Vis.colBuffer[i].name.StartsWith(meshName) && IsRockTooLarge(Vis.colBuffer[i].bounds)) {
flag = true;
}
Vis.colBuffer[i] = null;
}
if (!flag) {
float y = TerrainMeta.HighestPoint.y + 250f;
RaycastHit hit;
if (Physics.Raycast(position, Vector3.up, out hit, y, Layers.Mask.World, QueryTriggerInteraction.Ignore)) {
if (hit.collider.name.StartsWith(meshName) && IsRockTooLarge(hit.collider.bounds)) flag = true;
}
if (!flag && Physics.Raycast(position, Vector3.down, out hit, y, Layers.Mask.World, QueryTriggerInteraction.Ignore)) {
if (hit.collider.name.StartsWith(meshName) && IsRockTooLarge(hit.collider.bounds)) flag = true;
}
if (!flag && Physics.Raycast(position + new Vector3(0f, y, 0f), Vector3.down, out hit, y + 1f, Layers.Mask.World, QueryTriggerInteraction.Ignore)) {
if (hit.collider.name.StartsWith(meshName) && IsRockTooLarge(hit.collider.bounds)) flag = true;
}
}
return flag;
}
private static bool IsRockTooLarge(Bounds bounds, float extents = 1.5f) {
return bounds.extents.Max() > extents;
}
private bool IsKit(string kit) {
//Call kit plugin to check if its valid kit
var success = Kits?.Call("isKit", kit);
if (success == null || !(success is bool)) {
return false;
}
return (bool)success;
}
void GiveKit(NPCPlayer npc, string kit) {
if (kit == "" || !IsKit(kit)) return;
object success = Kits?.Call("GiveKit", npc, kit);
if (success == null || !(success is bool)) {
Puts("Failed to give NPC Kit");
return;
}
//Trys to equip stuff
Item projectileItem = null;
//Find first gun
foreach (var item in npc.inventory.containerBelt.itemList.ToList()) {
if (item.GetHeldEntity() is BaseProjectile) {
projectileItem = item;
break;
}
//Move medial items out of hot bar
if (item.GetHeldEntity() is MedicalTool) {
item.MoveToContainer(npc.inventory.containerMain);
continue;
}
}
if (projectileItem == null) {
//Find a melee weapon in the belt
foreach (var item in npc.inventory.containerBelt.itemList.ToList()) {
if (item.GetHeldEntity() is BaseMelee) {
projectileItem = item;
break;
}
}
}
if (projectileItem != null) {
//pull out active item.npc.UpdateActiveItem(projectileItem.uid);
npc.inventory.UpdatedVisibleHolsteredItems();
timer.Once(1f, () => {
npc.AttemptReload();
}
);
}
}
#endregion
#region Map Markers
private void CreateBotMarker(ScientistNPC npc) {
if (npc == null || npc.IsDestroyed) {
return;
}
MapMarkerGenericRadius mapMarker;
if (!botMarkerList.TryGetValue(npc, out mapMarker)) {
mapMarker = GameManager.server.CreateEntity(StringPool.Get(2849728229), npc.transform.position) as MapMarkerGenericRadius;
mapMarker.alpha = configData.MapSettings.BotDropZoneAlpha;
mapMarker.color1 = Color.yellow;
mapMarker.radius = configData.MapSettings.BotDropZoneRadius;
mapMarker.Spawn();
botMarkerList[npc] = mapMarker;
}
mapMarker.SendUpdate();
}
private void CreateDropZoneMarker(Vector3 pos) {
var mapMarker = GameManager.server.CreateEntity(StringPool.Get(2849728229), pos) as MapMarkerGenericRadius;
mapMarker.alpha = configData.MapSettings.DropZoneAlpha;
mapMarker.color1 = Color.red;
mapMarker.radius = configData.MapSettings.DropZoneRadius;
mapMarker.Spawn();
mapMarker.SendUpdate();
timer.Once(configData.npc.Lifetime + 90f, () => {
try {
dropZoneMarkerList.Remove(mapMarker);
mapMarker.Kill();
mapMarker.SendUpdate();
}
catch {
}
}
);
// add time to account for drop time
dropZoneMarkerList.Add(mapMarker);
}
private void RefreshBotMarkers() {
foreach (var npcPlayer in paratrooperList.Keys) {
CreateBotMarker(npcPlayer);
}
foreach (var jumperPlayer in jumperList) {
CreateBotMarker(jumperPlayer);
}
foreach (var mapMarker in dropZoneMarkerList) {
mapMarker?.SendUpdate();
}
}
private List<string> GetParatrooperPositions() {
var returnPositionList = new List<string>();
foreach (var jumper in jumperList) {
if (jumper == null || jumper.IsDestroyed) {
continue;
}
var gridPos = GetGridString(jumper.transform.position);
if (!returnPositionList.Contains(gridPos)) {
returnPositionList.Add(gridPos);
}
}
foreach (var ScientistNPC in paratrooperList.Keys) {
if (ScientistNPC == null || ScientistNPC.IsDestroyed) {
continue;
}
var gridPos = GetGridString(ScientistNPC.transform.position);
if (!returnPositionList.Contains(gridPos)) {
returnPositionList.Add(gridPos);
}
}
return returnPositionList;
}
private string GetParatrooperGridLocations() {
var troopPositionList = GetParatrooperPositions();
if (troopPositionList.Count == 0) {
return msg("NoParatroopers");
}
string returnValue = msg("ParatroopersFound");
foreach (string gridLoc in troopPositionList) {
returnValue = returnValue + " <color=orange>" + gridLoc + "</color>";
}
return returnValue;
}
#endregion
#region Wipe Elements
private void WipeMarkers(List<MapMarkerGenericRadius> markers) {
foreach (var mapMarker in markers) {
if (mapMarker == null || mapMarker.IsDestroyed) {
continue;
}
mapMarker.Kill();
mapMarker.SendUpdate();
}
markers.Clear();
}
private void WipeTroopers() {
foreach (var x in jumperList.ToList()) {
if (x != null && !x.IsDestroyed) {
x.Kill();
}
}
foreach (var entry in paratrooperList.ToList()) {
var x = entry.Key;
if (x != null && !x.IsDestroyed) {
x.Kill();
}
}
jumperList.Clear();
paratrooperList.Clear();
}
private void KillEvent() {
if (paratrooperList.Count > 0 || jumperList.Count > 0) {
WipeTroopers();
}
if (botMarkerList.Count > 0) {
WipeMarkers(botMarkerList.Values.ToList());
}
if (dropZoneMarkerList.Count > 0) {
WipeMarkers(dropZoneMarkerList);
}
foreach (var x in drops) {
if (x != null && !x.IsDestroyed) {
x.Kill();
}
}
foreach (var x in dropsCrate) {
if (x != null && !x.IsDestroyed) {
x.Kill();
}
}
foreach (var x in dropBradley) {
if (x != null && !x.IsDestroyed) {
x.Kill();
}
}
FltNum.Clear();
ParatrooperPlane[] planes = UnityEngine.Object.FindObjectsOfType<ParatrooperPlane>();
for (int i = 0; i < planes?.Length; i++) {
UnityEngine.Object.Destroy(planes[i]);
}
}
#endregion
#region Misc Functions
private Vector3 SpreadPosition(Vector3 origPosition, int amtToSpread) {
int spreadX = Oxide.Core.Random.Range(amtToSpread * -1, amtToSpread);
int spreadZ = Oxide.Core.Random.Range(amtToSpread * -1, amtToSpread);
return new Vector3(origPosition.x + spreadX, origPosition.y, origPosition.z + spreadZ);
}
private void RepeatRandomJump() {
// Call random jump
if (BasePlayer.activePlayerList.Count >= configData.Automation.RequiredPlayers) {
TriggerRandomJump();
}
else if (configData.Notifications.ConsoleOutput) {
Puts("Not enough players for event.");
}
// Queue up the next one..float nextJump = UnityEngine.Random.Range(configData.Automation.RandomJumpIntervalMinSec, configData.Automation.RandomJumpIntervalMaxSec);
timer.Once(nextJump, () => RepeatRandomJump());
if (configData.Notifications.ConsoleOutput) {
Puts("A random Paratroopers Event will occur in {0}s", nextJump);
}
}
private SpawnFilter filter = new SpawnFilter();
public Vector3 RandomDropPosition() {
Vector3 vector;
if (configData.Airfield) {
vector = GetAirfieldPosition();
if (vector != Vector3.zero) {
return vector;
}
}
float num = 100f;
float x = TerrainMeta.Size.x;
do {
vector = Vector3Ex.Range(0f - x / 3f, x / 3f);
}
while (filter.GetFactor(vector) == 0f && (num -= 1f) > 0f);
vector.y = 0f;
return vector;
}
private void TriggerRandomJump() {
var jumpPosition = RandomDropPosition();
if (configData.Notifications.ConsoleOutput) {
Puts("A random Paratroopers Event will occur near {0}", GetGridString(jumpPosition));
}
CallParatroopers(jumpPosition, configData.npc.RandomJumperCount, null, 0, true);
}
#endregion
#region Grid Teleport
public class SpawnPosition {
private const float aboveGoundPosition = 2.5f;
public Vector3 Position;
public Vector3 GroundPosition;
public string GridReference;
public SpawnPosition(Vector3 position) {
Position = position;
GroundPosition = GetGroundPosition(new Vector3(position.x, 25, position.z));
}
public bool isPositionAboveWater() {
if ((TerrainMeta.HeightMap.GetHeight(Position) - TerrainMeta.WaterMap.GetHeight(Position)) >= 0) return false;
return true;
}
public float WaterDepthAtPosition() {
return (TerrainMeta.WaterMap.GetHeight(Position) - TerrainMeta.HeightMap.GetHeight(Position));
}
Vector3 GetGroundPosition(Vector3 sourcePos) {
RaycastHit hitInfo;
if (Physics.Raycast(sourcePos, Vector3.down, out hitInfo)) sourcePos.y = hitInfo.point.y;
sourcePos.y = Mathf.Max(sourcePos.y, TerrainMeta.HeightMap.GetHeight(sourcePos)) + aboveGoundPosition;
return sourcePos;
}
}
#endregion
#region Drop Chutes
public class CrateDropChute : MonoBehaviour {
private SupplyDrop crate;
private Rigidbody rigidbody;
private BaseEntity parachute;
private void Awake() {
rigidbody = crate.GetComponent<Rigidbody>();
}
private void Start() {
AddParachute();
}
private void OnDestroy() {
RemoveParachute();
}
private void AddParachute() {
if (rigidbody != null) {
rigidbody.useGravity = true;
}
parachute = GameManager.server.CreateEntity(StringPool.Get(CHUTE_PREFABID));
if (parachute != null) {
parachute.SetParent(crate, false);
parachute.Spawn();
parachute.SendNetworkUpdateImmediate(true);
}
}
private void RemoveParachute() {
if (parachute.IsValid()) {
if (rigidbody != null) {
rigidbody.useGravity = false;
}
if (!parachute.IsDestroyed) {
parachute.Kill();
}
parachute = null;
}
}
private void OnCollisionEnter(Collision collision) {
Effect.server.Run(StringPool.Get(SUPPLYDROP_EFFECTID), transform.position);
Destroy(this);
}
private void FixedUpdate() {
if (parachute.IsValid()) {
rigidbody.useGravity = true;
rigidbody.mass = configData.LootBoxes.LootEffects.lootMass;
rigidbody.drag = configData.LootBoxes.LootEffects.lootDrag * (rigidbody.mass / 5f);
crate.transform.position -= new Vector3(0, 10f * configData.LootBoxes.LootEffects.lootMass * Time.deltaTime, 0);
//crate.transform.position -= new Vector3(0, 10f * Time.deltaTime, 0);
}
}
}
private class HackCrateDropChute : MonoBehaviour {
private BaseEntity crate;
private BaseEntity parachute;
private Rigidbody rigidbody;
private void Awake() {
crate = GetComponent<BaseEntity>();
rigidbody = crate.GetComponent<Rigidbody>();
rigidbody.useGravity = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rigidbody.mass = 5.25f;
rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
rigidbody.angularVelocity = Vector3Ex.Range(-1.75f, 1.75f);
rigidbody.drag = 2.0f * (rigidbody.mass / 5f);
rigidbody.angularDrag = 0.2f * (rigidbody.mass / 5f);
}
private void Start() {
AddParachute();
}
private void OnDestroy() {
RemoveParachute();
}
private void AddParachute() {
if (rigidbody != null) {
rigidbody.useGravity = false;
}
parachute = GameManager.server.CreateEntity(StringPool.Get(CHUTE_PREFABID));
if (parachute != null) {
parachute.SetParent(crate, false);
parachute.Spawn();
parachute.SendNetworkUpdateImmediate(true);
}
}
private void RemoveParachute() {
if (parachute.IsValid()) {
if (rigidbody != null) {
rigidbody.useGravity = true;
}
if (!parachute.IsDestroyed) {
parachute.Kill();
}
parachute = null;
}
}
private void OnCollisionEnter(Collision collision) {
Destroy(this);
}
private void FixedUpdate() {
if (parachute.IsValid()) {
crate.transform.position -= new Vector3(0, 10f * Time.deltaTime, 0);
}
}
}
private class DroppingVehicle : MonoBehaviour {
private BaseEntity vehicle;
private BaseEntity parachute;
private Rigidbody rb;
private void Awake() {
vehicle = GetComponent<BaseEntity>();
rb = vehicle.gameObject.GetComponent<Rigidbody>();
}
private void Start() {
AddParachute();
}
private void OnDestroy() {
RemoveParachute();
}
private void AddParachute() {
if (rb != null) {
rb.useGravity = false;
}
parachute = GameManager.server.CreateEntity(StringPool.Get(CHUTE_PREFABID));
if (parachute != null) {
parachute.SetParent(vehicle, "parachute_attach");
parachute.Spawn();
if (configData.LootBoxes.LootEffects.enableSmoke) {
Effect.server.Run(StringPool.Get(SMOKE_EFFECTID), parachute, 0, Vector3.zero, Vector3.zero, null, true);
}
}
}
private void RemoveParachute() {
if (vehicle.IsValid() && rb != null) {
rb.useGravity = true;
}
if (parachute.IsValid()) {
if (!parachute.IsDestroyed) {
parachute.Kill();
}
parachute = null;
}
}
private void OnCollisionEnter(Collision collision) {
Destroy(this);
}
private void FixedUpdate() {
if (parachute.IsValid()) {
vehicle.transform.position -= new Vector3(0, 10f * Time.deltaTime, 0);
}
}
}
#endregion
#region Jump Event Manager
private class JumpEventManager {
public Vector3 DropPosition = new Vector3();
public int ParatrooperCount = 0;
public int JumpedCount = 0;
public string ParatrooperKit = "";
public bool ParatrooperRandomKit;
public int ParatrooperHealth = 0;
private BaseEntity parachute;
private bool EnableLootDrop;
internal ParatrooperPlane paratrooperPlane {
get;
private set;
}
internal APCController APCBradley {
get;
private set;
}
private bool CreatedDropZoneMarker = false;
private void a_PositionReached(object sender, EventArgs e) {
Vector3 deployPos = paratrooperPlane.transform.position;
string jumpMessage = string.Format(ins.GetInboundMessage(), ins.GetGridString(deployPos));
foreach (int FlightNumber in ins.FltNum) {
if (configData.Notifications.Inbound) {
string key = configData.LootBoxes.LootEffects.dropBradley ? "InboundJumpPositionMessage2" : "InboundJumpPositionMessage";
SendChatMessage(key, FlightNumber, ParatrooperCount, ins.GetGridString(deployPos));
}
}
if (configData.Notifications.ConsoleOutput) {
ins.Puts(jumpMessage);
}
CreateJumpers();
}
private void CreateJumpers() {
var jumpPosition = ins.SpreadPosition(paratrooperPlane.transform.position, ins.GetDefaultJumpSpread());
ServerMgr.Instance.StartCoroutine(SpawnNPCs(jumpPosition, ParatrooperKit, ParatrooperHealth, ParatrooperRandomKit));
string ent = configData.LootBoxes.typeOfBox.ToLower();
if (++JumpedCount < ParatrooperCount) {
float nextJumperDelay = UnityEngine.Random.Range(0.01f, 0.5f);
ins.timer.Once(nextJumperDelay, () => CreateJumpers());
}
if (configData.MapSettings.CreateDropZoneMarkers && !CreatedDropZoneMarker && JumpedCount >= (ParatrooperCount / 2)) {
ins.CreateDropZoneMarker(jumpPosition);
CreatedDropZoneMarker = true;
if (configData.LootBoxes.enableLootBox) {
if (ent == "supplydrop") {
SpawnLoot();
}
else if (ent == "hackablecrate") {
SpawnLootCrate();
}
}
if (configData.LootBoxes.LootEffects.dropBradley) {
ins.timer.Once(0.05f, () => SpawnBradley());
}
}
}
private static void CreateSirenLights(BaseEntity entity) {
var SirenLight = GameManager.server.CreateEntity(StringPool.Get(SIRENLIGHT_EFFECTID), default(Vector3), default(Quaternion), true);
SirenLight.gameObject.Identity();
SirenLight.SetParent(entity as LootContainer, "parachute_attach");
SirenLight.Spawn();
SirenLight.SetFlag(BaseEntity.Flags.Reserved8, true);
}
private static void CreateSirenAlarms(BaseEntity entity) {
var SirenAlarm = GameManager.server.CreateEntity(StringPool.Get(SIRENALARM_EFFECTID), default(Vector3), default(Quaternion), true);
SirenAlarm.gameObject.Identity();
SirenAlarm.SetParent(entity as LootContainer, "parachute_attach");
SirenAlarm.Spawn();
SirenAlarm.SetFlag(BaseEntity.Flags.Reserved8, true);
}
private void CreateDropEffects(BaseEntity entity) {
if (entity == null) {
return;
}
if (configData.LootBoxes.LootEffects.enableSmoke) {
Effect.server.Run(StringPool.Get(SMOKE_EFFECTID), entity, 0, Vector3.zero, Vector3.zero, null, true);
}
if (configData.LootBoxes.LootEffects.enableSirenAlarm && configData.LootBoxes.LootEffects.enableSirenAlarmNight && TOD_Sky.Instance.IsNight) {
CreateSirenAlarms(entity);
}
else if (configData.LootBoxes.LootEffects.enableSirenAlarmNight && TOD_Sky.Instance.IsNight) {
CreateSirenAlarms(entity);
}
else if (configData.LootBoxes.LootEffects.enableSirenAlarm) {
CreateSirenAlarms(entity);
}
else return;
if (configData.LootBoxes.LootEffects.enableSirenLight && configData.LootBoxes.LootEffects.enableSirenLightNight && TOD_Sky.Instance.IsNight) {
CreateSirenLights(entity);
}
else if (configData.LootBoxes.LootEffects.enableSirenLightNight && TOD_Sky.Instance.IsNight) {
CreateSirenLights(entity);
}
else if (configData.LootBoxes.LootEffects.enableSirenLight) {
CreateSirenLights(entity);
}
else return;
}
private Vector3 GetRandomPoint(Vector3 position) {
var vector = position + UnityEngine.Random.onUnitSphere * 40f;
return vector;
}
private IEnumerator SpawnNPCs(Vector3 position, string jumperKit, int botHeal
// ... [Code preview truncated due to length. Full source in attached file] ...