C#:
using System.Collections.Generic;
using Newtonsoft.Json;
using CompanionServer.Handlers;
using System.Linq;
using UnityEngine;
using Oxide.Core.Plugins;
using Network;
using Rust;
using Rust.Modular;
using System.Collections;
using Newtonsoft.Json.Linq;
using Oxide.Game.Rust.Cui;
using Oxide.Core;
using System;
namespace Oxide.Plugins {
[Info("Convoy", "WebGhost", "1.1.8")]
class Convoy : RustPlugin {
[PluginReference]
Plugin NpcSpawn, GUIAnnouncements, DiscordMessages;
#region Variables
const bool en = true;
static Convoy ins;
int pathCount = 0;
bool active;
bool round = true;
int stopTime = 0;
int destroyTime = 0;
int eventTime = 0;
bool destroying = false;
bool failed = false;
bool hackedCrate = false;
List<Vector3> currentPath = new List<Vector3>();
List<ConvoyVehicle> convoyVehicles = new List<ConvoyVehicle>();
List<ScientistNPC> freeConvoyNpc = new List<ScientistNPC>();
HashSet<BasePlayer> players = new HashSet<BasePlayer>();
HashSet<BaseEntity> convoySummons = new HashSet<BaseEntity>();
DoorCloser doorCloser;
ConvoyModular convoyModular;
ConvoyHeli convoyHeli;
RootCar rootCar;
HeliConfig heliConfig;
BradleyConfig bradleyConfig;
ModularConfig modularConfig;
SupportModularConfig supportModularConfig;
SedanConfig sedanConfig;
Vector3 deathBradleyCoord = Vector3.zero;
Vector3 deathHeliCoord = Vector3.zero;
Coroutine stopCoroutine;
Coroutine eventCoroutine;
Coroutine destroyCoroutine;
#endregion Variables
#region API
private bool IsConvoyVehicle(BaseEntity entity) {
if (entity == null) return false;
return convoyVehicles.Any(x => x.baseEntity.net.ID == entity.net.ID);
}
private bool IsConvoyCrate(HackableLockedCrate crate) {
if (crate == null) return false;
if (convoyModular != null && convoyModular.crate.net.ID == crate.net.ID) return true;
return false;
}
private bool IsConvoyHeli(BaseHelicopter baseHelicopter) {
if (baseHelicopter == null) return false;
return convoyHeli != null && convoyHeli.baseHelicopter.net.ID == baseHelicopter.net.ID;
}
#endregion API
#region Hooks
void Init() {
ins = this;
Unsubscribes();
LoadData();
Unsubscribe("OnLootSpawn");
}
void OnServerInitialized() {
Subscribe("OnLootSpawn");
LoadDefaultMessages();
int vehicleCount = 0;
foreach (ConvoySetting convoySetting in _config.convoys) {
int count = convoySetting.firstBradleyCount + convoySetting.firstModularCount + convoySetting.firstSedanCount + 1 + convoySetting.endSedanCount + convoySetting.endModularCount + convoySetting.endBradleyCount;
if (count > vehicleCount) vehicleCount = count;
}
int rootCount = vehicleCount * _config.carDistance * 2 + vehicleCount + 20;
int roadCount = _config.roadCount <= rootCount ? rootCount : _config.roadCount;
if (_config.customRootName != "" && roots.ContainsKey(_config.customRootName)) {
List<List<string>> rootsPrefab = roots[_config.customRootName];
List<List<string>> goodRoads = rootsPrefab.Where(x => x.Count() > roadCount).ToList();
if (goodRoads.Count > 0) {
List<string> currentpathString = goodRoads.GetRandom();
foreach (string vectorString in currentpathString) currentPath.Add(vectorString.ToVector3());
}
}
if (_config.rounRoadPriority && currentPath.Count == 0) {
PathList pathList = TerrainMeta.Path.Roads.Where(x => x.Path.Points.Count() > roadCount && Vector3.Distance(x.Path.Points[0], x.Path.Points[x.Path.Points.Count() - 1]) < 10f && Vector3.Distance(x.Path.Points[0], x.Path.Points[x.Path.Points.Count() / 2]) > 50f).FirstOrDefault();
if (pathList != null && pathList.Path != null && pathList.Path.Points != null) currentPath = pathList.Path.Points.ToList();
}
if (currentPath.Count == 0) {
List<PathList> goodRoads = TerrainMeta.Path.Roads.Where(x => !_config.blockRoads.Contains(TerrainMeta.Path.Roads.IndexOf(x)) && x.Path.Points.Count() > roadCount && Vector3.Distance(x.Path.Points[0], x.Path.Points[x.Path.Points.Count() - 1]) > 100 && UnityEngine.Physics.RaycastAll(new Ray(x.Path.Points[10] + new Vector3(0, 1, 0), Vector3.down), 4f).Any(y => y.collider.name.Contains("Road Mesh")) && UnityEngine.Physics.RaycastAll(new Ray(x.Path.Points[x.Path.Points.Length / 2] + new Vector3(0, 1, 0), Vector3.down), 4f).Any(y => y.collider.name.Contains("Road Mesh"))).ToList();
if (goodRoads.Count > 0) {
PathList path = goodRoads.GetRandom();
currentPath = path.Path.Points.ToList();
pathCount = currentPath.Count();
}
}
pathCount = currentPath.Count();
if (pathCount == 0) {
PrintError("No road detected");
NextTick(() => Server.Command($"o.unload {Name}"));
return;
}
if (_config.version != Version.ToString()) {
if (_config.version == "1.1.6" || _config.version == "1.1.7") {
foreach (var a in _config.supportModularConfiguration) if (a.prefabName == null) a.prefabName = "assets/content/vehicles/modularcar/4module_car_spawned.entity.prefab";
foreach (var a in _config.modularConfiguration) if (a.prefabName == null) a.prefabName = "assets/content/vehicles/modularcar/4module_car_spawned.entity.prefab";
foreach (var a in _config.NPC) a.deleteCorpse = true;
_config.version = "1.1.8";
SaveConfig();
}
else {
PrintError("Delete the configuration file!");
NextTick(() => Server.Command($"o.unload {Name}"));
return;
}
}
if (Vector3.Distance(currentPath[0], currentPath[currentPath.Count() - 1]) > 10f) round = false;
timer.In(UnityEngine.Random.Range(_config.minStartTime, _config.minStartTime), () => {
if (!active) CreateConvoy();
else Puts("This event is active now");
}
);
}
void Unload() {
if (active) DeleteConvoy(false);
RootStop();
if (rootCar != null) rootCar.basicCar.Kill();
ins = null;
}
object OnEntityTakeDamage(BaseVehicleModule baseVehicleModule, HitInfo info) {
if (baseVehicleModule == null || info == null) return null;
BaseModularVehicle modularVehicle = baseVehicleModule.Vehicle;
if (modularVehicle != null && convoyVehicles.Any(x => x.baseEntity != null && x.baseEntity == modularVehicle)) {
if (info.InitiatorPlayer != null) {
float damageScale = convoyModular.baseEntity == modularVehicle ? modularConfig.damageMultiplier : supportModularConfig.damageMultiplier;
if (info.damageTypes.Has(DamageType.Explosion)) modularVehicle.health -= damageScale * info.damageTypes.Total() / 10;
else modularVehicle.health -= damageScale * info.damageTypes.Total() / 5;
if (!modularVehicle.IsDestroyed && modularVehicle.health <= 0) modularVehicle.Kill();
else ConvoyTakeDamage(modularVehicle, info);
}
return true;
}
return null;
}
object OnEntityTakeDamage(ModularCar entity, HitInfo info) {
if (convoyModular != null && convoyVehicles.Any(x => x.baseEntity == entity)) {
if (entity == convoyModular.baseEntity) info.damageTypes.ScaleAll(modularConfig.damageMultiplier);
else info.damageTypes.ScaleAll(supportModularConfig.damageMultiplier);
}
return ConvoyTakeDamage(entity, info);
}
object OnEntityTakeDamage(BasicCar entity, HitInfo info) {
if (info.InitiatorPlayer != null && convoySummons.Contains(entity)) {
if (!info.damageTypes.Has(DamageType.Explosion) && info.damageTypes.GetMajorityDamageType() == DamageType.Bullet) info.damageTypes.ScaleAll(250f);
else if (info.damageTypes.GetMajorityDamageType() == DamageType.Explosion && info.WeaponPrefab != null && info.WeaponPrefab.name == "rocket_hv") info.damageTypes.ScaleAll(10f);
return ConvoyTakeDamage(entity, info);
}
return null;
}
object OnEntityTakeDamage(BradleyAPC entity, HitInfo info) {
return ConvoyTakeDamage(entity, info);
}
object OnEntityTakeDamage(ScientistNPC entity, HitInfo info) {
if (info == null || entity == null) return null;
if ((info.InitiatorPlayer == null || !info.InitiatorPlayer.userID.IsSteamId()) && convoyVehicles.Count > 0 && convoyVehicles.Any(x => x.roamNpc.Contains(entity))) return true;
ConvoyVehicle convoyVehicle = convoyVehicles.Where(x => x.scientists.Contains(entity)).FirstOrDefault();
if (convoyVehicle != null) StopConvoy();
if (convoyVehicles.Any(x => x.driver == entity)) return true;
return null;
}
void OnEntityTakeDamage(BaseHelicopter entity, HitInfo info) {
if (convoyHeli == null || info == null || entity == null || convoyHeli.baseHelicopter != entity) return;
if (info.InitiatorPlayer != null) StopConvoy();
}
void OnEntityTakeDamage(BasePlayer player, HitInfo info) {
if (player == null || info == null || info.Initiator == null) return;
BradleyAPC bradleyAPC = info.Initiator as BradleyAPC;
if (bradleyAPC != null && convoyVehicles.Any(x => x.baseEntity == bradleyAPC)) {
if (!player.userID.IsSteamId()) info.damageTypes.ScaleAll(0);
else info.damageTypes.ScaleAll(bradleyConfig.scaleDamage);
}
}
void OnEntityKill(ModularCar entity) {
if (entity != null && convoyVehicles.Any(x => x.baseEntity == entity)) {
if (convoyModular != null && entity == convoyModular.baseEntity) {
failed = true;
if (convoyVehicles.Contains(convoyModular)) convoyVehicles.Remove(convoyModular);
StopConvoy();
if (!destroying && destroyCoroutine == null) {
destroying = true;
AlertToAllPlayers("Failed", _config.prefics);
AlertToAllPlayers("PreFinish", _config.prefics, GetTimeFromSecond(_config.preFinishTime));
destroyCoroutine = ServerMgr.Instance.StartCoroutine(DestroyCounter());
}
}
else ConvoyVehicleDie(entity);
}
}
void OnEntityKill(BradleyAPC entity) => ConvoyVehicleDie(entity);
void OnEntityKill(BasicCar entity) => ConvoyVehicleDie(entity);
void OnEntityKill(BaseHelicopter entity) {
if (entity == null || convoyHeli == null || convoyHeli.baseHelicopter != entity) return;
deathHeliCoord = entity.transform.position;
ConvoyVehicleDie(entity);
}
void OnEntityDeath(BasePlayer player, HitInfo info) {
if (player == null) return;
if (player.userID.IsSteamId() && players.Contains(player)) {
players.Remove(player);
if (_config.GUI.IsGUI) CuiHelper.DestroyUi(player, "TextMain");
}
else if (_config.blockSpawnDieNpc && info != null && info.Initiator != null && player is ScientistNPC) {
ConvoyVehicle convoyVehicle = convoyVehicles.FirstOrDefault(x => x.roamNpc.Contains(player));
if (convoyVehicle != null) convoyVehicle.NpcDie(player as ScientistNPC);
}
}
object CanHackCrate(BasePlayer player, HackableLockedCrate crate) {
if (crate == null || convoyModular == null || crate != convoyModular.crate) return null;
else if ((!_config.needStopConvoy || stopTime > 0) && (!_config.needKillCars || (convoySummons.Count == 0 && !convoySummons.Any(x => x != null && !x.IsDestroyed))) && (!_config.needKillNpc || (!freeConvoyNpc.Any(x => x != null && !x.IsDestroyed) && !convoyVehicles.Any(x => x.roamNpc.Any(y => x != null && !y.IsDestroyed))))) {
Unsubscribe("CanHackCrate");
AlertToAllPlayers("StartHackCrate", _config.prefics, player.displayName);
if (destroying) destroyTime += (int)modularConfig.crateUnlockTime + 30;
hackedCrate = true;
timer.In(modularConfig.crateUnlockTime, () => {
if (destroyCoroutine == null && !destroying) {
destroying = true;
destroyCoroutine = ServerMgr.Instance.StartCoroutine(DestroyCounter());
AlertToAllPlayers("PreFinish", _config.prefics, _config.preFinishTime);
}
}
);
if (stopTime <= 0) StopConvoy();
timer.In(0.5f, () => crate.hackSeconds = HackableLockedCrate.requiredHackSeconds - ins.modularConfig.crateUnlockTime);
return null;
}
else {
Alert(player, GetMessage("CantHackCrate", player.UserIDString, _config.prefics));
return true;
}
}
void OnEntitySpawned(HelicopterDebris entity) => NextTick(() => {
if (entity != null && !entity.IsDestroyed && deathBradleyCoord != null && (Vector3.Distance(entity.transform.position, deathBradleyCoord) < 20f || Vector3.Distance(entity.transform.position, deathHeliCoord) < 20f)) entity.Kill();
}
);
object CanMountEntity(BasePlayer player, BaseMountable entity) {
BaseEntity parrent = entity.VehicleParent();
if (parrent != null && player.userID.IsSteamId() && convoyVehicles.Any(x => x.baseEntity == parrent)) return true;
return null;
}
object OnHelicopterRetire(PatrolHelicopterAI ai) {
if (convoyHeli != null && convoyHeli.patrolHelicopterAI == ai) return true;
return null;
}
object CanHelicopterTarget(PatrolHelicopterAI heli, BasePlayer player) {
if (convoyHeli != null && heli != null && heli == convoyHeli.patrolHelicopterAI && ((_config.blockFirstAttack && stopTime == 0 && !failed && !hackedCrate) || player == null || !player.userID.IsSteamId() || player.IsSleeping())) return false;
return null;
}
object CanBradleyApcTarget(BradleyAPC apc, BaseEntity entity) {
if (apc != null && convoySummons.Count > 0 && convoySummons.Contains(apc)) {
if (_config.blockFirstAttack && stopTime == 0 && !failed && !hackedCrate) return false;
BasePlayer player = entity as BasePlayer;
if (player == null || !player.userID.IsSteamId() || player.IsSleeping()) return false;
}
return null;
}
object OnBotReSpawnCrateDropped(HackableLockedCrate crate) {
if (active && convoyModular != null && convoyModular.crate != null && convoyModular.crate == crate) return true;
return null;
}
#endregion Hooks
#region Commands
[ChatCommand("convoystart")]
void StartCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin) return;
if (active) {
Alert(player, GetMessage("EventActive", player.UserIDString, _config.prefics));
return;
}
if (arg != null && arg.Length >= 1) CreateConvoy(arg[0]);
else CreateConvoy();
}
[ChatCommand("convoystop")]
void StopCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin) return;
if (active) DeleteConvoy(true);
}
[ConsoleCommand("convoystart")]
void ConsoleStartCommand(ConsoleSystem.Arg arg) {
if (arg.Player() != null) return;
if (arg.Args != null && arg.Args.Count() > 0) CreateConvoy(arg.Args[0]);
CreateConvoy();
}
[ConsoleCommand("convoystop")]
void ConsoleStopCommand(ConsoleSystem.Arg arg) {
if (arg.Player() == null && active) DeleteConvoy(true);
}
[ChatCommand("convoyrootstart")]
void RootStartCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin || player.isInAir) return;
CreateRootCar(player);
}
[ChatCommand("convoyrootstop")]
void RootStopCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin || rootCar == null) return;
RootStop();
}
[ChatCommand("convoyrootsave")]
void RootSaveCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin || rootCar == null) return;
if (arg == null || arg.Count() == 0) {
Alert(player, $"{_config.prefics} To save the route, use the command: <color=#738d43>convoyrootsave [rootpresetname]</color>");
return;
}
if (rootCar.root.Count() < 50) Alert(player, $"{_config.prefics} The route is too short!");
else {
List<string> root = new List<string>();
foreach (Vector3 vector in rootCar.root) root.Add(vector.ToString());
if (!roots.ContainsKey(arg[0])) roots.Add(arg[0], new List<List<string>>());
roots[arg[0]].Add(root);
SaveData();
RootStop();
Alert(player, $"{_config.prefics} Route added to group <color=#738d43>{arg[0]}</color>");
}
}
[ChatCommand("convoyroadblock")]
void RoadBlockCommand(BasePlayer player, string command, string[] arg) {
if (!player.IsAdmin || player.isInAir) return;
PathList blockRoad = TerrainMeta.Path.Roads.FirstOrDefault(x => x.Path.Points.Any(y => Vector3.Distance(player.transform.position, y) < 10));
if (blockRoad == null) Alert(player, $"{_config.prefics} Road not found <color=#ce3f27>not found</color>");
int index = TerrainMeta.Path.Roads.IndexOf(blockRoad);
if (_config.blockRoads.Contains(index)) Alert(player, $"{_config.prefics} The road is already <color=#ce3f27>blocked</color>");
else if (blockRoad != null) {
_config.blockRoads.Add(index);
SaveConfig();
Alert(player, $"{_config.prefics} The road with the index <color=#738d43>{index}</color> is <color=#ce3f27>blocked</color>");
}
}
#endregion Commands
#region Method
void CreateConvoy(string presetName = "") {
if (active) {
Puts("This event is active now.To finish this event (convoystop), then to start the next one");
return;
}
if (!plugins.Exists("NpcSpawn")) {
PrintError("NpcSpawn plugin doesn`t exist! Please read the file ReadMe.txt");
Server.Command($"o.unload {Name}");
return;
}
destroyTime = ins._config.preFinishTime;
ConvoySetting convoySetting = null;
if (presetName != "") convoySetting = _config.convoys.Where(x => x.name == presetName).FirstOrDefault();
else if (_config.convoys.Any(x => x.chance > 0)) {
while (convoySetting == null) {
foreach (ConvoySetting setting in _config.convoys) {
if (UnityEngine.Random.Range(0.0f, 100.0f) <= setting.chance) convoySetting = setting;
}
}
}
if (convoySetting == null) {
PrintError("Event configuration not found!");
Server.Command($"o.reload {Name}");
return;
}
AlertToAllPlayers("PreStart", _config.prefics, GetTimeFromSecond(_config.preStartTime));
active = true;
timer.In(_config.preStartTime, () => {
if (!active) return;
Subscribes();
bradleyConfig = _config.bradleyConfiguration.Where(x => x.presetName == convoySetting.bradleyConfigurationName).FirstOrDefault();
sedanConfig = _config.sedanConfiguration.Where(x => x.presetName == convoySetting.sedanConfigurationName).FirstOrDefault();
modularConfig = _config.modularConfiguration.Where(x => x.presetName == convoySetting.modularConfigurationName).FirstOrDefault();
heliConfig = _config.heliesConfiguration.Where(x => x.presetName == convoySetting.heliConfigurationName).FirstOrDefault();
supportModularConfig = _config.supportModularConfiguration.Where(x => x.presetName == convoySetting.supportodularConfigurationName).FirstOrDefault();
int totalVehicleCount = convoySetting.firstBradleyCount + convoySetting.firstSedanCount + convoySetting.endSedanCount + convoySetting.endBradleyCount;
int startPoint = round ? UnityEngine.Random.Range(0, pathCount / 2) : 1;
//int startPoint = round ? (int)(pathCount * 0.98f) : 1;
int count = 0;
int totalCount = convoySetting.endBradleyCount;
int delataPoint = _config.carDistance;
int cycleCount = 0;
while (cycleCount < 25 && Vector3.Distance(currentPath[0], currentPath[delataPoint]) < 10) {
delataPoint += 2;
cycleCount++;
}
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateBradley(firstpoint, secondpoint);
startPoint += delataPoint;
}
totalCount += convoySetting.endModularCount;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateModular(firstpoint, secondpoint, false);
startPoint += delataPoint;
}
totalCount += convoySetting.endSedanCount;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateSedan(firstpoint, secondpoint);
startPoint += delataPoint;
}
totalCount++;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateModular(firstpoint, secondpoint);
startPoint += delataPoint;
}
totalCount += convoySetting.firstSedanCount;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateSedan(firstpoint, secondpoint);
startPoint += delataPoint;
}
totalCount += convoySetting.firstModularCount;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateModular(firstpoint, secondpoint, false);
startPoint += delataPoint;
}
totalCount += convoySetting.firstBradleyCount;
for (; count < totalCount; count++) {
int firstpoint = 0, secondpoint = 0;
DefineNextPathPoint(startPoint, pathCount, out firstpoint, out secondpoint);
CreateBradley(firstpoint, secondpoint);
startPoint += delataPoint;
}
if (convoySetting.heliOn && convoyModular != null) CreateHelicopter();
convoyVehicles.Reverse();
AlertToAllPlayers("EventStart", _config.prefics);
if (eventCoroutine != null) ServerMgr.Instance.StopCoroutine(eventCoroutine);
eventCoroutine = ServerMgr.Instance.StartCoroutine(EventCounter());
Puts("The event has begun");
Interface.CallHook("OnConvoyStart");
}
);
}
void DefineNextPathPoint(int point, int pathCount, out int firstPoint, out int endPoint) {
if (point > pathCount - 1) {
if (round) firstPoint = point - pathCount;
else {
PrintError("Insufficient route length!");
DeleteConvoy(true);
firstPoint = endPoint = 0;
}
}
else firstPoint = point;
int endpointClone = firstPoint++;
if (endpointClone > pathCount - 1) {
if (round) endPoint = endpointClone - pathCount;
else {
PrintError("Insufficient route length!");
DeleteConvoy(true);
firstPoint = endPoint = 0;
}
}
else endPoint = endpointClone;
}
void ReverseConvoy() {
currentPath.Reverse();
convoyVehicles.Reverse();
foreach (ConvoyVehicle convoyVehicle in convoyVehicles) {
if (convoyVehicle == null) continue;
Transform transform = convoyVehicle.baseEntity.transform;
transform.RotateAround(transform.position, transform.up, 180);
convoyVehicle.Rotate();
convoyVehicle.DefineFollowEntity();
}
}
void DeleteConvoy(bool unload = false) {
Unsubscribes();
if (active) Interface.CallHook("OnConvoyStop");
active = false;
if (doorCloser != null && !doorCloser.IsDestroyed) doorCloser.Kill();
foreach (BasePlayer player in ins.players) CuiHelper.DestroyUi(player, "TextMain");
players.Clear();
if (active) AlertToAllPlayers("Finish", _config.prefics);
destroying = false;
foreach (ScientistNPC scientist in freeConvoyNpc) {
if (scientist != null && !scientist.IsDestroyed) scientist.Kill();
}
foreach (ConvoyVehicle convoyVehicle in convoyVehicles) {
if (convoyVehicle != null && convoyVehicle.baseEntity != null && !convoyVehicle.baseEntity.IsDestroyed) {
convoyVehicle.destroyAll = true;
convoyVehicle.baseEntity.Kill();
}
}
if (convoyHeli != null && convoyHeli.baseHelicopter != null && !convoyHeli.baseHelicopter.IsDestroyed) convoyHeli.baseHelicopter.Kill();
convoyVehicles.Clear();
convoySummons.Clear();
Puts("The event is over");
if (stopCoroutine != null) ServerMgr.Instance.StopCoroutine(stopCoroutine);
if (eventCoroutine != null) ServerMgr.Instance.StopCoroutine(eventCoroutine);
if (destroyCoroutine != null) ServerMgr.Instance.StopCoroutine(destroyCoroutine);
if (convoyModular != null && convoyModular.baseEntity != null && !convoyModular.baseEntity.IsDestroyed) convoyModular.baseEntity.Kill();
if (unload) Server.Command($"o.reload {Name}");
}
void ConvoyVehicleDie(BaseEntity entity) {
if (entity == null) return;
ConvoyVehicle convoyVehicle = convoyVehicles.Where(x => x.baseEntity == entity).FirstOrDefault();
if (convoyVehicle != null) {
if (convoyVehicle is ConvoyBradley) deathBradleyCoord = entity.transform.position;
DefineFollow(entity);
}
if (convoySummons.Count > 0 && convoySummons.Contains(entity)) {
convoySummons.Remove(entity);
if (convoySummons.Count == 0 && convoyModular != null) {
StopConvoy();
convoyModular.StopMoving(false, true);
AlertToAllPlayers("SecurityKill", _config.prefics);
}
}
}
object ConvoyTakeDamage(BaseEntity entity, HitInfo info) {
if (entity == null || info == null || info.InitiatorPlayer == null || !convoyVehicles.Any(x => x.baseEntity == entity)) return null;
BasePlayer initiator = info.InitiatorPlayer;
if (!initiator.userID.IsSteamId()) return true;
if (info.ProjectileDistance <= 100f) StopConvoy(initiator);
return null;
}
void StopConvoy(BasePlayer initiator = null, int time = 0) {
stopTime = time > 0 ? time : _config.damamageStopTime;
if (stopCoroutine != null && stopTime != 0) return;
stopCoroutine = ServerMgr.Instance.StartCoroutine(StopCounter());
foreach (ConvoyVehicle convoyVehicle in convoyVehicles) if (convoyVehicle != null) convoyVehicle.StopMoving(true, true);
if (initiator != null && convoyHeli != null) convoyHeli.SetTarget(initiator);
if (convoyModular != null) CreateEventZone(convoyModular.baseEntity.transform.position - new Vector3(0f, 0.5f, 0f));
if (initiator != null) AlertToAllPlayers("ConvoyAttacked", _config.prefics, initiator.displayName);
}
void StartConvoy() {
if (failed || convoyModular == null || convoyModular.crate == null || convoyModular.crate.IsFullyHacked() || convoyModular.crate.IsBeingHacked()) return;
if (stopCoroutine != null) ServerMgr.Instance.StopCoroutine(stopCoroutine);
foreach (ScientistNPC scientist in freeConvoyNpc) if (scientist != null && !scientist.IsDestroyed) scientist.Kill();
freeConvoyNpc.Clear();
stopCoroutine = null;
stopTime = 0;
foreach (ConvoyVehicle convoyVehicle in convoyVehicles) convoyVehicle.StartMoving();
if (doorCloser != null && !doorCloser.IsDestroyed) doorCloser.Kill();
players.Clear();
}
void CreateEventZone(Vector3 position) {
if (doorCloser != null && !doorCloser.IsDestroyed) doorCloser.Kill();
if (convoyModular == null || convoyModular.baseEntity.IsDestroyed) return;
doorCloser = GameManager.server.CreateEntity("assets/prefabs/misc/doorcloser/doorcloser.prefab", position) as DoorCloser;
doorCloser.gameObject.AddComponent<ZoneController>();
}
void CreateBradley(int firstPoint, int secondPoint) {
Vector3 vector3 = currentPath[firstPoint];
ChechTrash(vector3);
BradleyAPC bradley = GameManager.server.CreateEntity("assets/prefabs/npc/m2bradley/bradleyapc.prefab", vector3, Quaternion.LookRotation(currentPath[firstPoint] - currentPath[secondPoint])) as BradleyAPC;
bradley.OwnerID = 755446;
bradley.skinID = 755446;
bradley.Spawn();
bradley.ClearPath();
bradley.currentPath = currentPath;
bradley.currentPathIndex = firstPoint;
ConvoyBradley convoyBradley = bradley.gameObject.AddComponent<ConvoyBradley>();
convoyVehicles.Add(convoyBradley);
convoyBradley.InitBradley();
convoySummons.Add(bradley);
}
void CreateSedan(int firstPoint, int secondPoint) {
Vector3 vector3 = currentPath[firstPoint];
ChechTrash(vector3);
BasicCar car = GameManager.server.CreateEntity("assets/content/vehicles/sedan_a/sedantest.entity.prefab", vector3, Quaternion.LookRotation(currentPath[firstPoint] - currentPath[secondPoint])) as BasicCar;
car.enableSaving = false;
car.OwnerID = 755446;
car.skinID = 755446;
car.Spawn();
ConvoyCar convoyCar = car.gameObject.AddComponent<ConvoyCar>();
convoyCar.currentPoint = firstPoint;
convoyCar.InitSedan();
convoyVehicles.Add(convoyCar);
convoySummons.Add(car);
}
void CreateModular(int firstPoint, int secondPoint, bool main = true) {
Vector3 vector3 = currentPath[firstPoint];
ChechTrash(vector3);
ModularCar car = GameManager.server.CreateEntity(main ? modularConfig.prefabName : supportModularConfig.prefabName, vector3, Quaternion.LookRotation(currentPath[firstPoint] - currentPath[secondPoint])) as ModularCar;
car.enableSaving = false;
car.spawnSettings.useSpawnSettings = false;
car.OwnerID = 755446;
car.skinID = 755446;
car.Spawn();
ConvoyModular modular = car.gameObject.AddComponent<ConvoyModular>();
modular.baseEntity = car;
modular.currentPoint = firstPoint;
convoyVehicles.Add(modular);
modular.InitModular(main);
if (main) convoyModular = modular;
else convoySummons.Add(car);
}
void CreateHelicopter() {
Vector3 position = convoyModular.baseEntity.transform.position + new Vector3(0, heliConfig.height, 0);
Quaternion rotation = convoyModular.baseEntity.transform.rotation;
BaseHelicopter heli = GameManager.server.CreateEntity("assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab", position, rotation) as BaseHelicopter;
heli.enableSaving = false;
heli.OwnerID = 755446;
heli.skinID = 755446;
heli.Spawn();
heli.transform.position = position;
convoyHeli = heli.gameObject.AddComponent<ConvoyHeli>();
heli._maxHealth = heliConfig.hp;
heli.startHealth = heliConfig.hp;
convoyHeli.InitHelicopter(heliConfig.hp);
convoySummons.Add(heli);
}
void DefineFollow(BaseEntity entity) {
ConvoyVehicle convoyVehicle = convoyVehicles.Where(x => x.baseEntity.net.ID == entity.net.ID).FirstOrDefault();
if (convoyVehicle == null) return;
int index = convoyVehicles.IndexOf(convoyVehicle);
index++;
if (index >= convoyVehicles.Count()) return;
ConvoyVehicle nextVehicle = convoyVehicles[index];
if (nextVehicle == null) return;
BaseEntity baseEntity = nextVehicle.baseEntity;
if (baseEntity == null || baseEntity.IsDestroyed) return;
convoyVehicles.Remove(convoyVehicle);
nextVehicle.DefineFollowEntity();
}
void RootStop() {
if (rootCar != null && !rootCar.basicCar.IsDestroyed) rootCar.basicCar.Kill();
}
void CreateRootCar(BasePlayer player) {
if (rootCar != null) {
Alert(player, $"{_config.prefics} The route is <color=#738d43>already</color> being recorded!");
return;
}
Alert(player, $"{_config.prefics} To build a route, drive a car along it and write to the chat: <color=#738d43>convoyrootsave [rootgroupname]</color>\nTo reset the route, print to the chat: <color=#738d43>convoyrootstop</color>");
BasicCar car = GameManager.server.CreateEntity("assets/content/vehicles/sedan_a/sedantest.entity.prefab", player.transform.position + new Vector3(0, 0.3f, 0), player.eyes.GetLookRotation()) as BasicCar;
car.enableSaving = false;
car.Spawn();
rootCar = car.gameObject.AddComponent<RootCar>();
rootCar.InitSedan(player);
BaseVehicle.MountPointInfo mountPointInfo = car.mountPoints[0];
player.MountObject(mountPointInfo.mountable);
mountPointInfo.mountable.MountPlayer(player);
}
IEnumerator EventCounter() {
while (eventTime < _config.eventTime && !destroying && active) {
eventTime++;
yield return CoroutineEx.waitForSeconds(1f);
}
if (!destroying && destroyCoroutine == null) {
destroying = true;
AlertToAllPlayers("PreFinish", _config.prefics, _config.preFinishTime);
destroyCoroutine = ServerMgr.Instance.StartCoroutine(DestroyCounter());
}
}
IEnumerator StopCounter() {
while (stopTime > 0) {
stopTime--;
yield return CoroutineEx.waitForSeconds(1f);
}
stopTime = 0;
if (active) StartConvoy();
}
IEnumerator DestroyCounter() {
while (destroyTime > 0) {
destroyTime--;
yield return CoroutineEx.waitForSeconds(1f);
}
destroyTime = 0;
if (active) DeleteConvoy(true);
}
#endregion Method
#region Classes
class ZoneController : FacepunchBehaviour {
DoorCloser mainCloser;
SphereCollider sphereCollider;
HashSet<BaseEntity> spheres = new HashSet<BaseEntity>();
void Awake() {
gameObject.layer = (int)Layer.Reserved1;
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.isTrigger = true;
sphereCollider.radius = ins._config.eventZone.radius;
mainCloser = GetComponent<DoorCloser>();
if (ins._config.GUI.IsGUI) InvokeRepeating(UpdateGui, 1f, 1f);
if (ins._config.eventZone.isDome) CreateSphere();
}
void CreateSphere() {
for (int i = 0; i < ins._config.eventZone.darkening; i++) {
BaseEntity sphere = GameManager.server.CreateEntity("assets/prefabs/visualization/sphere.prefab", mainCloser.transform.position);
SphereEntity entity = sphere.GetComponent<SphereEntity>();
entity.currentRadius = ins._config.eventZone.radius * 2;
entity.lerpSpeed = 0f;
sphere.enableSaving = false;
sphere.Spawn();
spheres.Add(sphere);
}
}
void UpdateGui() {
int time = ins._config.eventTime - ins.eventTime;
if (ins.destroying) time = ins.destroyTime;
foreach (BasePlayer player in ins.players) ins.MessageGUI(player, ins.GetMessage("GUI", player.UserIDString, ins.GetTimeFromSecond(time)));
}
void OnTriggerEnter(Collider other) {
BasePlayer player = other.GetComponentInParent<BasePlayer>();
if (player != null && player.userID.IsSteamId()) {
ins.players.Add(player);
if (ins._config.GUI.IsGUI) ins.MessageGUI(player, ins.GetMessage("GUI", player.UserIDString, ins.GetTimeFromSecond(ins._config.eventTime - ins.eventTime + ins.destroyTime)));
if (ins._config.eventZone.isCreateZonePVP) ins.Alert(player, ins.GetMessage("EnterPVP", player.UserIDString, ins._config.prefics));
}
}
void OnTriggerExit(Collider other) {
BasePlayer player = other.GetComponentInParent<BasePlayer>();
if (player != null && player.userID.IsSteamId()) {
ins.players.Remove(player);
if (ins._config.GUI.IsGUI) CuiHelper.DestroyUi(player, "TextMain");
if (ins._config.eventZone.isCreateZonePVP) ins.Alert(player, ins.GetMessage("ExitPVP", player.UserIDString, ins._config.prefics));
}
}
void OnDestroy() {
foreach (BaseEntity sphere in spheres) if (sphere != null && !sphere.IsDestroyed) sphere.Kill();
CancelInvoke(UpdateGui);
foreach (BasePlayer player in BasePlayer.activePlayerList) CuiHelper.DestroyUi(player, "TextMain");
}
}
class ConvoyVehicle : FacepunchBehaviour {
List<CoordConfig> coordNPC = new List<CoordConfig>();
List<BaseVehicle.MountPointInfo> baseMountables = new List<BaseVehicle.MountPointInfo>();
internal ConvoyVehicle followVehicle;
internal Rigidbody rigidbody;
internal BaseEntity baseEntity;
internal ScientistNPC driver;
internal bool destroyAll = false;
internal bool stop = true;
internal bool allConvoyStop = true;
int countDieNpc = 0;
internal List<ScientistNPC> scientists = new List<ScientistNPC>();
internal List<ScientistNPC> roamNpc = new List<ScientistNPC>();
NpcConfig npcConfig;
void Awake() {
Invoke(InitVehicle, 0.5f);
}
void InitVehicle() {
baseEntity = GetComponent<BaseEntity>();
if (baseEntity is BradleyAPC) {
npcConfig = ins._config.NPC.FirstOrDefault(x => x.name == ins.bradleyConfig.npcName);
coordNPC = ins.bradleyConfig.coordinates;
}
else if (baseEntity is ModularCar) {
if (ins.convoyModular != null && ins.convoyModular.baseEntity != null && baseEntity.net.ID == ins.convoyModular.baseEntity.net.ID) {
npcConfig = ins._config.NPC.FirstOrDefault(x => x.name == ins.modularConfig.npcName);
coordNPC = ins.modularConfig.coordinates;
}
else {
npcConfig = ins._config.NPC.FirstOrDefault(x => x.name == ins.supportModularConfig.npcName);
coordNPC = ins.supportModularConfig.coordinates;
}
}
else if (baseEntity is BasicCar) {
npcConfig = ins._config.NPC.FirstOrDefault(x => x.name == ins.sedanConfig.npcName);
coordNPC = ins.sedanConfig.coordinates;
}
rigidbody = baseEntity.gameObject.GetComponent<Rigidbody>();
rigidbody.mass = 3500;
rigidbody.centerOfMass = new Vector3(0, -0.2f, 0);
rigidbody.isKinematic = true;
DefineMountPoints();
StartMoving();
DefineFollowEntity();
if (!ins.round) InvokeRepeating(CheckRotate, 0.5f, 0.1f);
if (ins._config.deleteBarriers) InvokeRepeating(CheckBarriers, 3f, 3f);
}
internal virtual void Rotate() {
}
internal virtual void CheckRotate() {
}
internal virtual int GetCurrentPointIndex() {
return 0;
}
internal virtual void CheckBarriers() {
if (followVehicle != null) return;
foreach (Collider collider in UnityEngine.Physics.OverlapSphere(baseEntity.transform.position + baseEntity.transform.forward * 4f, 3f)) {
BaseEntity entity = collider.ToBaseEntity();
if (entity != null && !entity.IsDestroyed && ins._config.barriers.Contains(entity.ShortPrefabName) && !ins.convoyVehicles.Any(x => x.baseEntity == entity)) {
if (entity is BaseVehicle) {
BaseVehicle vehicle = entity as BaseVehicle;
if (!vehicle.mountPoints.Any(x => x.mountable.GetMounted() != null)) entity.Kill();
}
else entity.Kill();
}
}
}
internal virtual void OnDestroy() {
CancelInvoke(CheckRotate);
CancelInvoke(CheckBarriers);
KillScientists(!destroyAll);
}
#region Moving
internal void DefineFollowEntity() {
int index = ins.convoyVehicles.IndexOf(this);
if (index == 0) followVehicle = null;
else followVehicle = ins.convoyVehicles[--index];
}
internal void StopMoving(bool NPC = true, bool allConvoyStop = false) {
this.allConvoyStop = allConvoyStop;
CancelInvoke(BreakOff);
stop = true;
BreakOn();
if (NPC && ins.active) Invoke(CreateRoamNpc, 0.1f);
}
internal void StartMoving(bool delay = true) {
foreach (ScientistNPC scientist in roamNpc) if (scientist != null && !scientist.IsDestroyed) scientist.Kill();
roamNpc.Clear();
if (allConvoyStop && ins.active) CreatePassengers();
if (delay && ins.active) Invoke(BreakOff, 7f);
else BreakOff();
}
internal void BreakOff() {
allConvoyStop = false;
rigidbody.isKinematic = false;
stop = false;
}
void BreakOn() => rigidbody.isKinematic = true;
#endregion Moving
#region NPC
internal void NpcDie(ScientistNPC scientistNPC) {
if (!ins.active) return;
roamNpc.Remove(scientistNPC);
countDieNpc++;
}
void DefineMountPoints() {
BaseVehicle baseVehicle = baseEntity.gameObject.GetComponent<BaseVehicle>();
if (baseVehicle != null) baseMountables = baseVehicle.allMountPoints.ToList();
}
void CreatePassengers() {
int count = baseMountables.Count();
if (count == 0) return;
count -= countDieNpc;
if (count == 0) count = 1;
driver = null;
scientists.Clear();
for (int i = 0; i < count; i++) {
BaseVehicle.MountPointInfo mountPointInfo = baseMountables[i];
BaseMountable baseMountable = mountPointInfo.mountable;
if (baseMountable == null) continue
// ... [Code preview truncated due to length. Full source in attached file] ...