C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Ext.ChaosNPC;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("PlaneCrash", "k1lly0u", "0.3.12")]
[Description("Call cargo planes that can be shot down by players to score loot")]
class PlaneCrash : RustPlugin, IChaosNPCPlugin
{
#region Fields
[PluginReference] Plugin Clans, Friends, LustyMap;
private static PlaneCrash Instance { get; set; }
private Timer callTimer;
private bool initialized;
private readonly SpawnFilter Filter = new SpawnFilter
{
BiomeType = (TerrainBiome.Enum)15,
TopologyAll = (TerrainTopology.Enum)0,
TopologyAny = ~(TerrainTopology.Enum)0,
SplatType = (TerrainSplat.Enum)255,
TopologyNot = (TerrainTopology.Enum)2705030
};
// Effects
private const string C4EXPLOSION_EFFECT = "assets/prefabs/tools/c4/effects/c4_explosion.prefab";
private const string HELIEXPLOSION_EFFECT = "assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab";
private const string DEBRIS_EFFECT = "assets/prefabs/npc/patrol helicopter/damage_effect_debris.prefab";
private const string FIREBALL_EFFECT = "assets/bundled/prefabs/oilfireballsmall.prefab";
private const string SMOKE_EFFECT = "assets/bundled/prefabs/fx/smoke_signal_full.prefab";
// Prefabs
private const string CARGOPLANE_PREFAB = "assets/prefabs/npc/cargo plane/cargo_plane.prefab";
private const string GIBS_PREFAB = "assets/prefabs/npc/patrol helicopter/servergibs_patrolhelicopter.prefab";
private const string CRATE_PREFAB = "assets/prefabs/npc/patrol helicopter/heli_crate.prefab";
private const string SUPPLYDROP_PREFAB = "assets/prefabs/misc/supply drop/supply_drop.prefab";
private const string DEBRISMARKER_PREFAB = "assets/prefabs/tools/map/explosionmarker.prefab";
#endregion
#region Oxide Hooks
private void Loaded()
{
LoadData();
foreach (KeyValuePair<string, int> kvp in configData.Cooldowns)
{
if (!kvp.Key.StartsWith("planecrash."))
permission.RegisterPermission("planecrash." + kvp.Key, this);
else permission.RegisterPermission(kvp.Key, this);
}
permission.RegisterPermission("planecrash.cancall", this);
if (!configData.Plane.ApplyOnSpawn)
Unsubscribe(nameof(OnEntitySpawned));
lang.RegisterMessages(Messages, this);
}
private void OnServerInitialized()
{
Instance = this;
initialized = true;
if (configData.EventTimers.Random)
StartCrashTimer();
}
private void OnServerSave() => SaveData();
private void OnEntitySpawned(CargoPlane cargoPlane)
{
NextFrame(() =>
{
if (cargoPlane)
{
SpawnCrashPlane(cargoPlane);
Debug.Log("[PlaneCrash] - Replaced game spawned plane");
if (configData.Messages.DisplayIncoming)
SendChatMessage("IncomingMessage.Supply");
}
});
}
private object CanLootEntity(BasePlayer player, LootContainer container)
{
LootLock lootLock = container.GetComponent<LootLock>();
if (lootLock && !lootLock.IsUnlocked())
{
if (player.userID == lootLock.ownerID)
return null;
if (configData.Loot.LockSettings.LockToPlayerTeam)
{
RelationshipManager.PlayerTeam ownerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(lootLock.ownerID);
RelationshipManager.PlayerTeam playerTeam = RelationshipManager.ServerInstance.FindPlayersTeam(player.userID);
if (ownerTeam != null && playerTeam != null && ownerTeam == playerTeam)
return null;
}
if (configData.Loot.LockSettings.LockToPlayerClans && IsClanmate(lootLock.ownerID, player.userID))
return null;
if (configData.Loot.LockSettings.LockToPlayerFriends && AreFriends(lootLock.ownerID, player.userID))
return null;
player.ChatMessage(msg("LootLocked", player.UserIDString));
return false;
}
return null;
}
private void Unload()
{
CrashPlane[] planes = UnityEngine.Object.FindObjectsOfType<CrashPlane>();
for (int i = 0; i < planes?.Length; i++)
planes[i].Kill(BaseNetworkable.DestroyMode.None);
configData = null;
Instance = null;
}
#endregion
#region CustomNPC
public bool InitializeStates(BaseAIBrain customNPCBrain) => false;
public bool WantsToPopulateLoot(CustomScientistNPC customNpc, NPCPlayerCorpse npcplayerCorpse) => false;
public byte[] GetCustomDesign() => null;
#endregion
#region Components
public enum FlightStatus { Flying, Crashing }
public class CrashPlane : BaseEntity
{
public Transform Transform { get; private set; }
public BoxCollider Collider { get; private set; }
public FlightStatus status;
private BasePlayer lastAttacker;
public Vector3 startPosition;
public Vector3 endPosition;
public float secondsToTake;
public float secondsTaken;
private FireballHandler engineFireballLeft;
private FireballHandler engineFireballRight;
private DestructionTrigger destructionTrigger;
private int rocketHitsTaken;
private float speed;
private float currentSpeed;
private float crashTimeTaken = 0;
private readonly float crashTimeToTake = 20;
private bool isDying;
private bool isSmoking;
public bool shouldDropCrate;
private bool hasDropped;
private bool willAutoCrash;
private float autoCrashAt;
private float randomCrashDirection = -1;
private float crashDelay = 0;
private Vector3 crashRotation;
public bool isPaused = false;
private void Awake()
{
Transform = transform;
gameObject.layer = 0;
gameObject.name = "CrashPlane";
CreateColliders();
speed = currentSpeed = configData.Plane.Speed;
status = FlightStatus.Flying;
willAutoCrash = Random.Range(0, 100) < configData.Plane.AutoCrashChance;
autoCrashAt = Random.Range(configData.Plane.CrashMinimumRange, configData.Plane.CrashMaximumRange);
if (configData.Plane.RandomizeCrashDirection)
randomCrashDirection = Random.value;
}
private void CreateColliders()
{
Collider = gameObject.AddComponent<BoxCollider>();
Collider.size = new Vector3(25, 15, 55);
Collider.center = Vector3.up * 10f;
destructionTrigger = gameObject.CreateChild().AddComponent<DestructionTrigger>();
destructionTrigger.Setup(this);
}
public override void OnAttacked(HitInfo info)
{
base.OnAttacked(info);
if (info == null)
return;
TimedExplosive timedExplosive = info.WeaponPrefab as TimedExplosive;
if (timedExplosive)
{
SmallExplosion();
rocketHitsTaken++;
BasePlayer attacker = info.InitiatorPlayer;
if (configData.Loot.CrateHit > 0)
ServerMgr.Instance.StartCoroutine(SpawnLoot(configData.Loot.CrateHit, false));
if (configData.Loot.SupplyHit > 0)
ServerMgr.Instance.StartCoroutine(SpawnLoot(configData.Loot.SupplyHit, true));
if (rocketHitsTaken == 1 && engineFireballLeft == null)
engineFireballLeft = new FireballHandler(new Vector3(-14.5f, 7.5f, 9f), Transform);
if (rocketHitsTaken >= configData.Plane.DestroyHits)
{
if (attacker)
lastAttacker = attacker;
if (configData.Messages.DisplayAttacker && lastAttacker)
SendChatMessage("AttackerMessage2", lastAttacker.displayName);
Die();
return;
}
if (rocketHitsTaken >= configData.Plane.DownHits && status == FlightStatus.Flying)
{
if (attacker)
lastAttacker = attacker;
if (lastAttacker && configData.Messages.DisplayAttacker)
SendChatMessage("AttackerMessage1", lastAttacker.displayName);
BeginCrash();
return;
}
}
}
public override void PostInitShared()
{
base.PostInitShared();
if (configData.Plane.SmokeTrail)
Invoke(()=> RunEffect(SMOKE_EFFECT, this, Vector3.zero, Vector3.up * 3), 5f);
}
public override void DestroyShared()
{
Destroy(destructionTrigger);
base.DestroyShared();
}
private void Update()
{
if (isPaused)
return;
if (Transform.position.y <= 0)
{
if (!isDying)
Die();
return;
}
if (status == FlightStatus.Crashing)
{
crashTimeTaken += Time.deltaTime;
float delta = Mathf.InverseLerp(0, crashTimeToTake, crashTimeTaken);
if (delta < 1)
{
currentSpeed = speed + Mathf.Lerp(0, 10, delta);
Vector3 direction = crashRotation;
direction.x = Mathf.Lerp(0, 25f, delta);
if (randomCrashDirection != -1)
{
float yaw = Mathf.Lerp(0f, 25f, delta * 1.75f);
float roll = Mathf.Lerp(0f, 35f, delta * 2.25f);
if (randomCrashDirection <= 0.5f)
{
direction.y -= yaw;
direction.z += roll;
}
else
{
direction.y += yaw;
direction.z -= roll;
}
}
Transform.eulerAngles = direction;
}
Transform.position = Vector3.MoveTowards(Transform.position, Transform.position + (Transform.forward * 10), currentSpeed * Time.deltaTime);
}
else
{
Transform.position = Vector3.MoveTowards(Transform.position, endPosition, currentSpeed * Time.deltaTime);
float delta = InverseLerp(startPosition, endPosition, Transform.position);
secondsTaken = secondsToTake * delta;
if (shouldDropCrate && !hasDropped && delta >= 0.5f)
{
hasDropped = true;
SupplyDrop supplyDrop = GameManager.server.CreateEntity(SUPPLYDROP_PREFAB, Transform.position + (Transform.forward * -10f) + (Vector3.down * 3f)) as SupplyDrop;
supplyDrop.globalBroadcast = true;
supplyDrop.Spawn();
supplyDrop.Invoke(() => VerifySupplyDropParachute(supplyDrop), 2f);
Interface.CallHook("OnSupplyDropDropped", supplyDrop, this);
}
if (willAutoCrash)
{
if (delta >= autoCrashAt && crashDelay < Time.time)
{
if (TerrainMeta.HeightMap.GetHeight(Transform.position) < 0)
{
crashDelay = Time.time + 3f;
return;
}
SendChatMessage("AutoCrashMessage");
BeginCrash();
}
}
}
engineFireballLeft?.Update();
engineFireballRight?.Update();
Transform.hasChanged = true;
}
private void VerifySupplyDropParachute(SupplyDrop supplyDrop)
{
supplyDrop.SetFlag(Flags.Reserved2, true);
/*if (!supplyDrop.parachute || supplyDrop.parachute.IsDestroyed)
{
supplyDrop.parachute = GameManager.server.CreateEntity(supplyDrop.parachutePrefab.resourcePath);
supplyDrop.parachute.SetParent(supplyDrop, "parachute_attach", false, false);
supplyDrop.parachute.Spawn();
supplyDrop.isLootable = false;
supplyDrop.Invoke(()=> VerifySupplyDropParachute(supplyDrop), 1f);
}*/
}
private void OnDestroy()
{
enabled = false;
CancelInvoke();
if (engineFireballLeft != null)
engineFireballLeft.Destroy();
if (engineFireballRight != null)
engineFireballRight.Destroy();
if (!IsDestroyed)
Kill();
}
#region Initialization
public void SetFlightPath(Vector3 dropPosition)
{
if (configData.Plane.RandomDistance != 0)
{
Vector2 random = Random.insideUnitCircle * configData.Plane.RandomDistance;
dropPosition.x += random.x;
dropPosition.z += random.y;
}
dropPosition = dropPosition.XZ3D();
startPosition = Vector3Ex.Range(-1f, 1f);
startPosition.y = 0f;
startPosition.Normalize();
startPosition *= (TerrainMeta.Size.x * 2f);
startPosition.y = 150f + configData.Plane.Height;
endPosition = startPosition * -1f;
endPosition.y = startPosition.y;
startPosition += dropPosition;
endPosition += dropPosition;
secondsToTake = Vector3.Distance(startPosition, endPosition) / speed;
transform.position = startPosition;
transform.rotation = Quaternion.LookRotation(endPosition - startPosition);
if (configData.Plane.RandomizeCrashDirection)
{
Vector3 perp = Vector3.Cross(Transform.forward, Vector3.zero - Transform.position);
randomCrashDirection = Vector3.Dot(perp, Vector3.up) >= 0 ? 1 : 0f;
}
Destroy(this, secondsToTake);
}
public void SetFlightPath(CargoPlane cargoPlane)
{
startPosition = cargoPlane.startPos;
startPosition.y = 150f + configData.Plane.Height;
endPosition = cargoPlane.endPos;
endPosition.y = startPosition.y;
secondsToTake = Vector3.Distance(startPosition, endPosition) / speed;
secondsToTake = cargoPlane.secondsToTake;
willAutoCrash = false;
Vector3 position = cargoPlane.transform.position;
transform.position = new Vector3(position.x, startPosition.y, position.z);
transform.rotation = cargoPlane.transform.rotation;
if (configData.Plane.RandomizeCrashDirection)
{
Vector3 perp = Vector3.Cross(Transform.forward, Vector3.zero - Transform.position);
randomCrashDirection = Vector3.Dot(perp, Vector3.up) >= 0 ? 1 : 0f;
}
Destroy(this, secondsToTake);
}
public void BeginCrash()
{
endPosition = new Vector3(endPosition.x, 0, endPosition.z);
status = FlightStatus.Crashing;
crashRotation = Transform.eulerAngles;
SmallExplosion();
if (engineFireballLeft == null)
engineFireballLeft = new FireballHandler(new Vector3(-14.5f, 7.5f, 9f), Transform);
if (engineFireballRight == null)
engineFireballRight = new FireballHandler(new Vector3(14.5f, 7.5f, 9f), Transform);
}
#endregion
#region Effects
private void BigExplosion()
{
RunEffect(HELIEXPLOSION_EFFECT, null, Transform.position);
RunEffect(DEBRIS_EFFECT, null, Transform.position);
}
private void SmallExplosion()
{
RunEffect(C4EXPLOSION_EFFECT, null, Transform.position);
RunEffect(DEBRIS_EFFECT, null, Transform.position);
}
#endregion
private float InverseLerp(Vector3 a, Vector3 b, Vector3 value)
{
Vector3 ab = b - a;
Vector3 av = value - a;
return Vector3.Dot(av, ab) / Vector3.Dot(ab, ab);
}
private void Die()
{
if (isDying)
return;
isDying = true;
BigExplosion();
FinalLootNPCSpawn();
if (configData.LustyOptions.CrashIcon)
InvokeHandler.Invoke(this, UpdateCrashMarker, 1.5f);
if (configData.MapOptions.CrashIcon)
InvokeHandler.Invoke(this, UpdateMapMarker, 1.5f);
InvokeHandler.Invoke(this, SmallExplosion, 0.25f);
InvokeHandler.Invoke(this, SmallExplosion, 0.5f);
InvokeHandler.Invoke(this, BigExplosion, 1.25f);
InvokeHandler.Invoke(this, SmallExplosion, 1.75f);
InvokeHandler.Invoke(this, BigExplosion, 2.25f);
Destroy(this, 2.5f);
}
#region Loot
private void FinalLootNPCSpawn()
{
Vector3 velocity = Transform.forward * currentSpeed;
if (configData.Loot.FireLife > 0)
{
List<ServerGib> serverGibs = ServerGib.CreateGibs(GIBS_PREFAB, gameObject, gameObject, velocity, 5f);
for (int i = 0; i < 12; i++)
{
FireBall fireBall = GameManager.server.CreateEntity(FIREBALL_EFFECT, Transform.position, Transform.rotation, true) as FireBall;
if (fireBall)
{
Vector3 randsphere = Random.onUnitSphere;
fireBall.transform.position = (Transform.position + new Vector3(0f, 1.5f, 0f)) + (randsphere * Random.Range(-4f, 4f));
Collider collider = fireBall.GetComponent<Collider>();
fireBall.lifeTimeMin = fireBall.lifeTimeMax = configData.Loot.FireLife;
fireBall.Spawn();
fireBall.SetVelocity(velocity + (randsphere * Random.Range(3f, 10f)));
foreach (ServerGib serverGib in serverGibs)
Physics.IgnoreCollision(collider, serverGib.GetCollider(), true);
}
}
}
List<BaseEntity> list = Facepunch.Pool.Get<List<BaseEntity>>();
if (configData.Loot.CrateCrash > 0)
ServerMgr.Instance.StartCoroutine(SpawnLoot(configData.Loot.CrateCrash, false, true, list));
if (configData.Loot.SupplyCrash > 0)
ServerMgr.Instance.StartCoroutine(SpawnLoot(configData.Loot.SupplyCrash, true, true, list));
if (configData.NPCOptions.Enabled)
Instance.timer.In(5f, () => SpawnNPCs(list, lastAttacker));
else Facepunch.Pool.FreeUnmanaged(ref list);
if (configData.Messages.DisplayDestroy)
{
if (configData.Messages.UseGrid)
SendChatMessage("DestroyMessage.Grid", MapHelper.PositionToString(Transform.position));
else SendChatMessage("DestroyMessage", Transform.position.x, Transform.position.z);
}
}
private IEnumerator SpawnLoot(int amount, bool isDrop, bool isCrashing = false, List<BaseEntity> lootList = null)
{
Vector3 lastMoveDir = Transform.forward * currentSpeed;
for (int j = 0; j < amount; j++)
{
Vector3 position = (Transform.position + new Vector3(0f, 1.5f, 0f)) + (Random.onUnitSphere * Random.Range(-2f, 3f));
position.y = Mathf.Max(TerrainMeta.HeightMap.GetHeight(position), position.y) + 3f;
string ent = isDrop ? SUPPLYDROP_PREFAB : CRATE_PREFAB;
LootContainer container = GameManager.server.CreateEntity(ent, position, Quaternion.LookRotation(Random.onUnitSphere), true) as LootContainer;
container.enableSaving = false;
container.Spawn();
if (lootList != null)
lootList.Add(container);
if (configData.Loot.LockSettings.LockToPlayer && lastAttacker)
LootLock.Initialize(container, lastAttacker.userID);
if (j == 0 && configData.Plane.Smoke && isCrashing && !isSmoking)
{
RunEffect(SMOKE_EFFECT, container);
isSmoking = true;
}
Rigidbody rigidbody;
if (!isDrop)
{
rigidbody = container.gameObject.GetComponent<Rigidbody>();
if (!rigidbody)
rigidbody = container.gameObject.AddComponent<Rigidbody>();
}
else
{
container.GetComponent<SupplyDrop>().RemoveParachute();
rigidbody = container.GetComponent<Rigidbody>();
}
if (rigidbody)
{
rigidbody.isKinematic = false;
rigidbody.useGravity = true;
rigidbody.mass = 2f;
rigidbody.interpolation = RigidbodyInterpolation.Interpolate;
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rigidbody.velocity = Vector3.ClampMagnitude(lastMoveDir, 35) + (Random.onUnitSphere * Random.Range(1f, configData.Loot.LootSpread));
rigidbody.angularVelocity = Vector3Ex.Range(-1.75f, 1.75f);
rigidbody.drag = 0.5f * (rigidbody.mass / 5f);
rigidbody.angularDrag = 0.2f * (rigidbody.mass / 5f);
}
if (configData.Loot.FireLife > 0f)
FireballHandler.Create(container);
InvokeHandler.Invoke(container, () => FillLootContainer(container, isDrop), 2f);
if (configData.Loot.LootDespawnTime > 0)
{
container.Invoke(() =>
{
if (!container || container.HasFlag(BaseEntity.Flags.Open))
return;
container.Kill();
}, configData.Loot.LootDespawnTime);
}
yield return CoroutineEx.waitForEndOfFrame;
}
}
#endregion
#region Map Markers
private void UpdateCrashMarker()
{
if (!Instance.LustyMap)
return;
Instance.LustyMap.Call("AddTemporaryMarker", Transform.position.x, Transform.position.z, "Crashed Plane", configData.LustyOptions.IconURL, 0);
Instance.timer.In(configData.LustyOptions.CrashIconTime, () => Instance.LustyMap.Call("RemoveTemporaryMarker", "Crashed Plane"));
}
private void UpdateMapMarker()
{
BaseEntity baseEntity = GameManager.server.CreateEntity(DEBRISMARKER_PREFAB, Transform.position, Quaternion.identity, true);
baseEntity.Spawn();
baseEntity.SendMessage("SetDuration", configData.MapOptions.CrashIconTime, SendMessageOptions.DontRequireReceiver);
}
#endregion
private class DestructionTrigger : MonoBehaviour
{
private CrashPlane crashPlane;
private BoxCollider collider;
public void Setup(CrashPlane crashPlane)
{
this.crashPlane = crashPlane;
gameObject.layer = (int)Layer.Reserved2;
Rigidbody rigidbody = gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = false;
rigidbody.isKinematic = true;
rigidbody.detectCollisions = true;
rigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
collider = gameObject.AddComponent<BoxCollider>();
collider.isTrigger = true;
collider.size = new Vector3(60, 15, 55);
collider.center = Vector3.up * 10f;
Physics.IgnoreCollision(crashPlane.Collider, collider, true);
}
private void OnTriggerEnter(Collider col)
{
if (col is TerrainCollider)
{
crashPlane.Die();
return;
}
BaseEntity baseEntity = col.gameObject.ToBaseEntity();
if (baseEntity is BasePlayer)
{
if ((baseEntity as BasePlayer).IsNpc)
return;
(baseEntity as BasePlayer).Die();
return;
}
if (configData.Plane.Destruction)
{
if (baseEntity is TreeEntity or ResourceEntity or BuildingBlock or SimpleBuildingBlock or PatrolHelicopter)
{
BaseCombatEntity baseCombatEntity = baseEntity as BaseCombatEntity;
if (baseCombatEntity)
{
baseCombatEntity.Die();
return;
}
if (baseEntity)
{
baseEntity.Kill(BaseNetworkable.DestroyMode.Gib);
return;
}
}
}
}
}
private class FireballHandler
{
private readonly Vector3 offset;
private readonly Transform parent;
private FireBall fireBall;
public FireballHandler(Vector3 offset, Transform parent)
{
this.offset = offset;
this.parent = parent;
fireBall = Create(parent.TransformPoint(offset), 600f);
}
public void Update()
{
if (fireBall)
fireBall.transform.position = parent.TransformPoint(offset);
}
public void Destroy()
{
if (fireBall && !fireBall.IsDestroyed)
{
fireBall.Extinguish();
fireBall = null;
}
}
public static FireBall Create(Vector3 position, float lifetime)
{
FireBall fireBall = GameManager.server.CreateEntity(FIREBALL_EFFECT, position) as FireBall;
fireBall.enableSaving = false;
fireBall.Spawn();
Rigidbody rb = fireBall.GetComponent<Rigidbody>();
rb.isKinematic = true;
fireBall.CancelInvoke(fireBall.Extinguish);
fireBall.CancelInvoke(fireBall.TryToSpread);
fireBall.Invoke(fireBall.Extinguish, lifetime);
return fireBall;
}
public static FireBall Create(BaseEntity parent)
{
FireBall fireBall = GameManager.server.CreateEntity(FIREBALL_EFFECT) as FireBall;
fireBall.SetParent(parent, false, false);
fireBall.transform.localPosition = Vector3.zero;
fireBall.lifeTimeMin = fireBall.lifeTimeMax = configData.Loot.FireLife;
fireBall.Spawn();
fireBall.GetComponent<Rigidbody>().isKinematic = true;
fireBall.GetComponent<Collider>().enabled = false;
parent.SetFlag(Flags.OnFire, true, false, true);
parent.SetFlag(Flags.Locked, true, false, true);
parent.Invoke(() =>
{
if (fireBall && !fireBall.IsDestroyed)
fireBall.Kill(DestroyMode.None);
if (parent && !parent.IsDestroyed)
{
parent.SetFlag(Flags.OnFire, false, false, true);
parent.SetFlag(Flags.Locked, false, false, true);
}
}, configData.Loot.FireLife);
return fireBall;
}
}
}
public class LootLock : MonoBehaviour
{
internal LootContainer Container { get; private set; }
internal ulong ownerID;
private float lockExpiretime;
private void Awake()
{
Container = GetComponent<LootContainer>();
lockExpiretime = Time.time + configData.Loot.LockSettings.LockTime;
Destroy(this, configData.Loot.LockSettings.LockTime);
}
internal bool IsUnlocked() => Time.time >= lockExpiretime;
internal static void Initialize(LootContainer container, ulong ownerId)
{
LootLock lootLock = container.gameObject.AddComponent<LootLock>();
lootLock.ownerID = ownerId;
}
}
#region NPCs
private static void SpawnNPCs(List<BaseEntity> lootContainers, BasePlayer lastAttacker)
{
Vector3 position = Vector3.zero;
int count = 0;
lootContainers.ForEach((BaseEntity baseEntity) =>
{
if (baseEntity)
{
position += baseEntity.transform.position;
count++;
}
});
position /= count;
Facepunch.Pool.FreeUnmanaged(ref lootContainers);
ServerMgr.Instance.StartCoroutine(CreateNPCs(position, lastAttacker));
}
private static IEnumerator CreateNPCs(Vector3 position, BasePlayer lastAttacker)
{
int amount = configData.NPCOptions.Amount + (configData.NPCOptions.CorpseEnabled ? configData.NPCOptions.CorpseAmount : 0);
for (int i = 0; i < amount; i++)
{
Vector3 newPosition = position + (Random.onUnitSphere * 20);
bool spawnDead = i >= configData.NPCOptions.Amount;
NPCSettings settings = configData.NPCOptions.CustomNPCSettings;
settings.StartDead = spawnDead;
ChaosNPC.SpawnNPC(Instance, newPosition, settings);
yield return CoroutineEx.waitForSeconds(0.5f);
}
}
#endregion
#endregion
#region Functions
private Vector3 GetRandomDropPosition()
{
Vector3 dropPosition;
int attempts = 100;
float mapSize = TerrainMeta.Size.x;
do
{
dropPosition = Vector3Ex.Range(-(mapSize / 6f), mapSize / 6f);
}
while (Filter.GetFactor(dropPosition, true) == 0f && (attempts -= 1) > 0f);
dropPosition.y = 0f;
return dropPosition;
}
private void StartCrashTimer()
{
callTimer = timer.In(Random.Range(configData.EventTimers.Min, configData.EventTimers.Max) * 60, () =>
{
Vector3 position = GetRandomDropPosition();
SpawnCrashPlane(position, configData.Loot.DropOnAutoSpawn);
Debug.Log($"[PlaneCrash] - Auto-spawned plane with a drop target at {position}");
if (configData.Messages.DisplayIncoming)
SendChatMessage("IncomingMessage");
StartCrashTimer();
});
}
private CrashPlane SpawnCrashPlane(Vector3 dropPosition, bool dropCrate)
{
CargoPlane cargoPlane = (CargoPlane)GameManager.server.CreateEntity(CARGOPLANE_PREFAB, Vector3.zero, Quaternion.identity, true);
CrashPlane crashPlane = cargoPlane.gameObject.AddComponent<CrashPlane>();
crashPlane.prefabID = cargoPlane.prefabID;
crashPlane.bounds = new Bounds(new Vector3(0f, 10f, 0f), new Vector3(60f, 15f, 55f));
crashPlane.syncPosition = true;
crashPlane.globalBroadcast = true;
crashPlane.shouldDropCrate = dropCrate;
crashPlane.SetFlightPath(dropPosition);
UnityEngine.Object.DestroyImmediate(cargoPlane);
crashPlane.Spawn();
return crashPlane;
}
private CrashPlane SpawnCrashPlane(CargoPlane replaceCargoPlane)
{
CargoPlane cargoPlane = (CargoPlane)GameManager.server.CreateEntity(CARGOPLANE_PREFAB, replaceCargoPlane.transform.position, replaceCargoPlane.transform.rotation, true);
CrashPlane crashPlane = cargoPlane.gameObject.AddComponent<CrashPlane>();
crashPlane.prefabID = cargoPlane.prefabID;
crashPlane.bounds = new Bounds(new Vector3(0f, 10f, 0f), new Vector3(60f, 15f, 55f));
crashPlane.syncPosition = true;
crashPlane.globalBroadcast = true;
crashPlane.shouldDropCrate = true;
crashPlane.SetFlightPath(replaceCargoPlane);
UnityEngine.Object.DestroyImmediate(cargoPlane);
replaceCargoPlane.Kill();
crashPlane.Spawn();
return crashPlane;
}
private static void RunEffect(string name, BaseEntity entity = null, Vector3 position = new Vector3(), Vector3 offset = new Vector3())
{
if (entity)
Effect.server.Run(name, entity, 0, offset, position, null, true);
else Effect.server.Run(name, position, Vector3.up, null, true);
}
private static void FillLootContainer(BaseEntity entity, bool isDrop)
{
if (!entity)
return;
ItemContainer container = isDrop && entity is SupplyDrop ? (entity as SupplyDrop).inventory : entity is LootContainer ? (entity as LootContainer).inventory : null;
if (configData.Loot.DespawnSafezone && IsInSafeZone(entity.transform.position))
{
container.Clear();
entity.Kill();
return;
}
ConfigData.LootSettings.LootTables lootTable = isDrop ? configData.Loot.SupplyLoot : configData.Loot.CrateLoot;
if (container == null || lootTable == null)
return;
if (lootTable.Enabled)
{
while (container.itemList.Count > 0)
{
Item item = container.itemList[0];
item.RemoveFromContainer();
item.Remove(0f);
}
List<ConfigData.LootSettings.LootTables.LootItem> items = new List<ConfigData.LootSettings.LootTables.LootItem>(lootTable.Items);
int count = Random.Range(lootTable.Minimum, lootTable.Maximum);
for (int i = 0; i < count; i++)
{
ConfigData.LootSettings.LootTables.LootItem lootItem = items.GetRandom();
if (lootItem == null)
continue;
items.Remove(lootItem);
if (items.Count == 0)
items.AddRange(lootTable.Items);
Item item = ItemManager.CreateByName(lootItem.Shortname, Random.Range(lootItem.Min, lootItem.Max));
if (item != null)
item.MoveToContainer(container);
}
}
if (configData.Loot.LockSettings.LockCrates)
{
entity.SetFlag(BaseEntity.Flags.Locked, true, false);
InvokeHandler.Invoke(entity, () => { entity.SetFlag(BaseEntity.Flags.Locked, false, false); }, configData.Loot.LockSettings.LockTimer);
}
}
private static bool IsInSafeZone(Vector3 position)
{
int count = Physics.OverlapSphereNonAlloc(position, 1f, Vis.colBuffer, 1 << 18, QueryTriggerInteraction.Collide);
for (int i = 0; i < count; i++)
{
Collider collider = Vis.colBuffer[i];
if (collider.GetComponent<TriggerSafeZone>())
return true;
}
return false;
}
private static void StripInventory(BasePlayer player, bool skipWear = false)
{
List<Item> allItems = Pool.Get<List<Item>>();
player.inventory.GetAllItems(allItems);
for (int i = allItems.Count - 1; i >= 0; i--)
{
Item item = allItems[i];
if (skipWear && item?.parent == player.inventory.containerWear)
continue;
item.RemoveFromContainer();
item.Remove();
}
Pool.FreeUnmanaged(ref allItems);
}
private bool IsCrashPlane(BaseEntity baseEntity) => baseEntity is CrashPlane;
private string FormatTime(double time)
{
TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
int days = dateDifference.Days;
int hours = dateDifference.Hours;
hours += (days * 24);
int mins = dateDifference.Minutes;
int secs = dateDifference.Seconds;
return string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
}
private static void SendChatMessage(string key, params object[] args)
{
for (int i = 0; i < BasePlayer.activePlayerList.Count; i++)
{
BasePlayer player = BasePlayer.activePlayerList[i];
player.ChatMessage(args != null ? string.Format(msg(key, player.UserIDString), args) : msg(key, player.UserIDString));
}
}
#endregion
#region Friends
private bool AreFriends(ulong playerId, ulong friendId)
{
if (Friends)
return (bool)Friends?.Call("AreFriends", playerId.ToString(), friendId.ToString());
return false;
}
private bool IsClanmate(ulong playerId, ulong friendId)
{
if (Clans)
{
object playerTag = Clans?.Call("GetClanOf", playerId);
object friendTag = Clans?.Call("GetClanOf", friendId);
if (playerTag is string && friendTag is string)
{
if (!string.IsNullOrEmpty((string)playerTag) && !string.IsNullOrEmpty((string)friendTag) && (playerTag == friendTag))
return true;
}
}
return false;
}
#endregion
#region Commands
[ConsoleCommand("callcrash")]
void ccmdSendCrash(ConsoleSystem.Arg arg)
{
if (arg.Connection != null)
return;
Vector3 location = Vector3.zero;
if (arg.Args != null)
{
if (arg.Args.Length == 1 && arg.Args[0].ToLower() == "help")
{
SendReply(arg, $"{Title} v{Version} - k1lly0u @ chaoscode.io");
SendReply(arg, "callcrash - Send a random crash plane");
SendReply(arg, "callcrash \"X\" \"Z\" - Send a crash plane towards the specified X and Z co-ordinates");
SendReply(arg, "callcrash \"playername\" - Send a crash plane towards the specified player's position");
SendReply(arg, "callcrash crashall - Force crash any active planes");
return;
}
if (arg.Args.Length > 0)
{
if (arg.Args.Length == 2)
{
if (float.TryParse(arg.Args[0], out float x) && float.TryParse(arg.Args[1], out float z))
{
location = new Vector3(x, 0, z);
SendReply(arg, $"Crash plane sent to X: {x}, Z: {z}");
}
}
if (arg.Args.Length == 1)
{
if (arg.Args[0].ToLower() == "crashall")
{
CrashPlane[] crashPlanes = UnityEngine.Object.FindObjectsOfType<CrashPlane>();
if (crashPlanes == null || crashPlanes.Length == 0)
{
SendReply(arg, "There are no planes currently active");
return;
}
for (int i = 0; i < crashPlanes.Length; i++)
{
CrashPlane crashPlane = crashPlanes[i];
if (crashPlane.status == FlightStatus.Flying)
crashPlane.BeginCrash();
}
SendReply(arg, $"Force crashing {crashPlanes.Length} planes!");
return;
}
else
{
IPlayer targetPlayer = covalence.Players.FindPlayer(arg.Args[0]);
if (targetPlayer != null && targetPlayer.IsConnected)
{
BasePlayer target = targetPlayer?.Object as BasePlayer;
if (target)
{
location = target.transform.position;
SendReply(arg, $"Crash plane sent towards {target.displayName}'s current position");
}
}
else
{
SendReply(arg, "Could not locate the specified player");
return;
}
}
}
else
{
location = GetRandomDropPosition();
SendReply(arg, "Crash plane sent to random location");
}
}
}
else
{
location = GetRandomDropPosition();
SendReply(arg, "Crash plane sent to random location");
}
SpawnCrashPlane(location, false);
}
[ChatCommand("callcrash")]
void cmdSendCrash(BasePlayer player, string command, string[] args)
{
int cooldown = -1;
if (player.IsAdmin || permission.UserHasPermission(player.UserIDString, "planecrash.cancall"))
cooldown = 0;
else
{
foreach (KeyValuePair<string, int> kvp in configData.Cooldowns.OrderBy(x => x.Value))
{
if (permission.UserHasPermission(player.UserIDString, kvp.Key))
{
cooldown = kvp.Value;
break;
}
}
}
if (cooldown < 0)
return;
if (args.Length == 1 && args[0].ToLower() == "help")
{
SendReply(player, $"<color=#ce422b>{Title}</color> <color=#939393>v</color><color=#ce422b>{Version}</color> <color=#939393>-</color> <color=#ce422b>k1lly0u</color><color=#939393> @</color> <color=#ce422b>chaoscode.io</color>");
if (player.IsAdmin || permission.UserHasPermission(player.UserIDString, "planecrash.cancall"))
{
SendReply(player, "<color=#ce422b>/callcrash</color><color=#939393> - Send a random crash plane</color>");
SendReply(player, "<color=#ce422b>/callcrash \"X\" \"Z\"
// ... [Code preview truncated due to length. Full source in attached file] ...