C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using UnityEngine;
using Random = Oxide.Core.Random;
using Oxide.Plugins.XDCobaltLaboratoryExtensionMethods;
namespace Oxide.Plugins {
[Info("XDCobaltLaboratory", "DezLife", "2.6.0")]
internal class XDCobaltLaboratory : RustPlugin {
#region Var
[PluginReference]
Plugin CopyPaste, IQChat, NpcSpawn;
private static XDCobaltLaboratory _;
private List<uint> Light = new List<uint> {
3887352222, 1889323056, 630866573, 4027991414, 1392608348, 1748062128
}
;
private HashSet<Vector3> busyPoints3D = new HashSet<Vector3>();
private List<BaseEntity> HouseCobaltLab = new List<BaseEntity>();
private List<ScientistNPC> npcs = new List<ScientistNPC>();
private NpcZones npcZones = null;
private List<ulong> HideUIUser = new List<ulong>();
private string PosIvent;
private HackableLockedCrate CrateEnt;
private const int MaxRadius = 7;
private Timer SpawnHouseTime;
private Timer RemoveHouseTime;
private Configuration.BuildingPasteSettings.PasteBuild labIndex = null;
private const bool Ru = false;
private Coroutine setupBuildingRoutine {
get;
set;
}
= null;
#endregion
#region Lang
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["XD_IVENT_START"]
= "<color=#008000>[Cobalt Lab Event] </color>Ученые разбили на этом острове свою лабораторию под названием Кобальт, скорее всего там находятся ценные вещи, ведь она охраняется!\nКвадрат: <color=#00FF00>{0}</color>", ["XD_IVENT_STARTUI"] = "Ученые развернули свою Кобальтовую лабораторию!\nКвадрат : {0}", ["XD_IVENT_NOPLAYER"] = "<color=#008000>[Cobalt Lab Event] </color>Ученые закончили свой эксперимент и успешно покинули остров без происшествий", ["XD_IVENT_CRATEHACK"] = "<color=#008000>[Cobalt Lab Event] </color>В лаборатории кобальт <color=#FFD700>{0}</color> начал взлом секретного ящика в квадрате <color=#00FF00>{1}</color>", ["XD_IVENT_CRATEHACKHELP"] = "<color=#008000>[Cobalt Lab Event] </color>В лаборатории кобальт <color=#FFD700>{0}</color> начал взлом секретного ящика в квадрате <color=#00FF00>{1}</color>\nНа это место уже прибыла подмога! Будте осторожней", ["XD_IVENT_CRATEHACKEND"] = "<color=#008000>[Cobalt Lab Event] </color>В лаборатории кобальт был взломан секретный ящик, ученые начинают эвакуацию с острова, у вас осталось <color=#9ACD32>{0} минут</color>, чтобы забрать его!", ["XD_IVENT_CRATELOOTFOUND"] = " <color=#008000>[Cobalt Lab Event] </color>В лаборатории кобальт никто не успел залутать взломанный ящик, лаборатория была эвакуирована и постройка разрушена", ["XD_IVENT_CRATELOOTPLAYER"] = "<color=#008000>[Cobalt Lab Event] </color><color=#FFD700>{0}</color> успешно ограбил лабораторию кобальт и забрал ценные вещи с секретного ящика", ["XD_IVENT_HOUSECOBALT"] = "Cobalt laboratory", ["XD_IVENT_START_DISCORD"] = "Ученые разбили на этом острове свою лабораторию под названием Кобальт,скорее всего там находится ценные вещи, ведь он охраняется!\nКвадрат : {0}", ["XD_IVENT_NOPLAYER_DISCORD"] = "Ученые закончили свой эксперимент и успешно покинули остров без происшествий", ["XD_IVENT_CRATEHACK_DISCORD"] = "В лаборатории кобальт {0} начал взлом секретного ящика в квадрате {1}\nСоберитесь с силами и отбейте его", ["XD_IVENT_CRATEHACKHELP_DISCORD"] = "В лаборатории кобальт {0} начал взлом секретного ящика в квадрате {1}\nСоберитесь с силами и отбейте его\nНа это место уже прибыла подмога! Будте осторожней", ["XD_IVENT_CRATEHACKEND_DISCORD"] = "В лаборатории кобальт был взломан секретный ящик, ученые начинают эвакуацию с острова, у вас осталось {0} минут, чтобы забрать его!", ["XD_IVENT_CRATELOOTFOUND_DISCORD"] = "В лаборатории кобальт никто не успел залутать взломанный ящик, лаборатория была эвакуирована и постройка разрушена", ["XD_IVENT_CRATELOOTPLAYER_DISCORD"] = "{0} успешно ограбил лабораторию кобальт и забрал ценные вещи с секретного ящика", ["XD_IVENT_CL_ITEM_SYNTAX"] = "Используйте:\n/cl.items add - добавить лут к существующему\n/cl.items reset - заменить старый лут на новый", ["XD_IVENT_CL_ITEM_ADDED"] = "Вы успешно добавили новые предметы для ящика.\nОбязательно настройте их в конфиге", ["XD_IVENT_CL_ITEM_REPLACED"] = "Вы успешно заменили все предметы на новые.\nОбязательно настройте их в конфиге", ["XD_IVENT_CLBOT_ITEM_SYNTAX"] = "Используйте:\n/cl.botitems add \"названия присета\" - добавить лут к существующему\n/cl.botitems reset - заменить старый лут на новый", ["XD_IVENT_CLBOT_ITEM_ADDED"] = "Вы успешно добавили новые предметы для npc.\nОбязательно настройте их в конфиге", ["XD_IVENT_CLBOT_ITEM__PRESET_NOTFOUND"] = "Присета с именем `{0}`, не существует.", ["XD_IVENT_CLBOT_ITEM_REPLACED"] = "Вы успешно заменили все предметы на новые.\nОбязательно настройте их в конфиге", ["XD_IVENT_CLCONTROLLER_SYNTAX"] = "Используйте:\n/cl start - Запуск ивента досрочно\n/cl stop - отменить ивент досрочно", ["XD_IVENT_CLCONTROLLER_ACTIVE"] = "Ивент уже активен!", ["XD_IVENT_CLCONTROLLER_STOPADMIN"] = "Ивент окончен досрочно администратором!", ["XD_IVENT_CLCONTROLLER_NOT_ACTIVE"] = "Нет активных ивентов", ["XD_IVENT_CLCONTROLLER_NOT_ENOUGH_PLAYERS"] = "Недостаточно игроков для запуска ивента!", ["XD_IVENT_CLCONTROLLER_BUILDING_ERROR"] = "Ошибка #1 \nПостройка не смогла заспавнится\nОбратитесь к разработчику\nDezLife#1480\nvk.com/dezlife", ["XD_IVENT_CLCONTROLLER_NOT_BUILDING_BOX"] = "Ошибка #3, В постройке не найден ящик", ["XD_IVENT_CLCONTROLLER_THE_POINTS_ARE_ENDED"] = "Все точки закончены!\nНачинаем генерировать новые...", ["XD_IVENT_CLCONTROLLER_ENTER_PVP"] = "Вы <color=#ce3f27>вошли</color> в PVP зону, теперь другие игроки <color=#ce3f27>могут</color> наносить вам урон!", ["XD_IVENT_CLCONTROLLER_EXIT_PVP"] = "Вы <color=#738d43>вышли</color> из PVP зоны, теперь другие игроки <color=#738d43>не могут</color> наносить вам урон!", ["XD_IVENT_CLCONTROLLER_NOT_NPSSPAWN"] = "У вас отсутсвует плагин NpcSpawn.Пожалуйста установите его для коректной работы", ["XD_EVENT_BUILDING_NOTFOUND"] = "Не найден файл постройки : {0}.Переместите файл с постройкой в папку `/oxide/data/copypaste`",
}
, this, "ru");
lang.RegisterMessages(new Dictionary<string, string> {
["XD_IVENT_START"]
= "<color=#008000>[Cobalt Lab Event] </color>Scientists have set up their laboratory on this island called Cobalt, most likely there are valuable things there, because it is protected!\nGrid: <color=#00FF00>{0}</color>", ["XD_IVENT_STARTUI"] = "Scientists have deployed their Cobalt Lab!\nGrid : {0}", ["XD_IVENT_NOPLAYER"] = "<color=#008000>[Cobalt Lab Event] </color>The scientists completed their experiment and successfully left the island without incident", ["XD_IVENT_CRATEHACK"] = "<color=#008000>[Cobalt Lab Event] </color>In the cobalt lab <color=#FFD700>{0}</color> started hacking a secret crate in the grid <color=#00FF00>{1}</color>", ["XD_IVENT_CRATEHACKHELP"] = "<color=#008000>[Cobalt Lab Event] </color>In the cobalt lab <color=#FFD700>{0}</color> started hacking a secret crate in the grid <color=#00FF00>{1}</color>\nHelp has already arrived at this place! Be careful", ["XD_IVENT_CRATEHACKEND"] = "<color=#008000>[Cobalt Lab Event] </color>A secret crate has been hacked in the cobalt lab, scientists are beginning to evacuate the island, you have <color=#9ACD32>{0} minutes</color> left to pick it up!", ["XD_IVENT_CRATELOOTFOUND"] = " <color=#008000>[Cobalt Lab Event] </color>In the cobalt laboratory, no one had time to hack the secret crate, the laboratory was evacuated and the building was destroyed", ["XD_IVENT_CRATELOOTPLAYER"] = "<color=#008000>[Cobalt Lab Event] </color><color=#FFD700>{0}</color> successfully robbed the cobalt lab and took valuables from a secret crate", ["XD_IVENT_HOUSECOBALT"] = "Cobalt laboratory", ["XD_IVENT_START_DISCORD"] = "Scientists have set up their laboratory on this island called Cobalt, most likely there are valuable things there, because it is guarded!\nКвадрат : {0}", ["XD_IVENT_NOPLAYER_DISCORD"] = "Scientists finished their experiment and successfully left the island without incident.", ["XD_IVENT_CRATEHACK_DISCORD"] = "In the laboratory, cobalt {0} started breaking into a secret box in square {1}\nGather your strength and fight it off", ["XD_IVENT_CRATEHACKHELP_DISCORD"] = "In the laboratory, cobalt {0} started breaking into a secret box in square {1}\nGather your strength and fight it off\nHelp has already arrived at this place! Be careful", ["XD_IVENT_CRATEHACKEND_DISCORD"] = "A secret box has been broken in the cobalt laboratory, scientists are starting to evacuate the island, you have {0} minutes left to pick it up!", ["XD_IVENT_CRATELOOTFOUND_DISCORD"] = " In the cobalt laboratory, no one had time to patch up the cracked box, the laboratory was evacuated and the building was destroyed", ["XD_IVENT_CRATELOOTPLAYER_DISCORD"] = "{0} successfully robbed the cobalt laboratory and took valuable items from the secret box", ["XD_IVENT_CL_ITEM_SYNTAX"] = "Use:\n/cl.items add - add loot to the existing one\n/cl.items reset - replace old loot with a new one", ["XD_IVENT_CL_ITEM_ADDED"] = "You have successfully added new items to the crate.\nBe sure to configure them in the config", ["XD_IVENT_CL_ITEM_REPLACED"] = "You have successfully replaced all items with new ones.\nBe sure to configure them in the config", ["XD_IVENT_CLBOT_ITEM_SYNTAX"] = "Use:\n/cl.botitems add \"preset name\" - add loot to the existing one\n/cl.botitems reset - replace old loot with a new one", ["XD_IVENT_CLBOT_ITEM_ADDED"] = "You have successfully added new items for the npc.\nBe sure to configure them in the config", ["XD_IVENT_CLBOT_ITEM__PRESET_NOTFOUND"] = "There is no preset named `{0}`.", ["XD_IVENT_CLBOT_ITEM_REPLACED"] = "You have successfully replaced all items with new ones.\nBe sure to configure them in the config", ["XD_IVENT_CLCONTROLLER_SYNTAX"] = "Use:\n/cl start - Launching the event ahead of schedule\n/cl stop - cancel the event ahead of schedule", ["XD_IVENT_CLCONTROLLER_ACTIVE"] = "The event is already active!", ["XD_IVENT_CLCONTROLLER_STOPADMIN"] = "Event ended ahead of schedule by the administrator!", ["XD_IVENT_CLCONTROLLER_NOT_ACTIVE"] = "No active events", ["XD_IVENT_CLCONTROLLER_NOT_ENOUGH_PLAYERS"] = "Not enough players to start the event!", ["XD_IVENT_CLCONTROLLER_BUILDING_ERROR"] = "Error #1 \nThe building could not spawn\nContact the developer\nDezLife#1480\nvk.com/dezlife", ["XD_IVENT_CLCONTROLLER_NOT_BUILDING_BOX"] = "Error #3,No crate found in the building", ["XD_IVENT_CLCONTROLLER_THE_POINTS_ARE_ENDED"] = "All points are completed!\nWe start generating new...", ["XD_IVENT_CLCONTROLLER_ENTER_PVP"] = "You <color=#ce3f27>entered</color> In the PVP zone, now other players <color=#ce3f27>can</color> do damage to you!", ["XD_IVENT_CLCONTROLLER_EXIT_PVP"] = "You <color=#738d43>exited</color> from the PVP zone, now other players <color=#738d43>can not</color> do damage to you!", ["XD_IVENT_CLCONTROLLER_NOT_NPSSPAWN"] = "You are missing the Npc Spawn plugin.Please install it to work correctly", ["XD_EVENT_BUILDING_NOTFOUND"] = "The construction file was not found : {0}.Move the file with the building to the folder `/oxide/data/copypaste`",
}
, this);
}
#endregion
#region Data
private void LoadDataCopyPaste() {
foreach (var building in config.pasteSettings.pasteBuilds) {
if (!Interface.Oxide.DataFileSystem.ExistsDatafile("copypaste/" + building.housepath)) {
PrintError(GetLang("XD_EVENT_BUILDING_NOTFOUND", null, building.housepath));
NextTick(() => {
Interface.Oxide.UnloadPlugin(Name);
}
);
return;
}
}
}
#endregion
#region Configuration
private Configuration config;
private class Configuration {
[JsonProperty(Ru ? "Настройка запуска и остановки ивента" : "Setting up and stopping an event")]
public IventController iventController = new IventController();
[JsonProperty(Ru ? "Настройка уведомлений" : "Configuring notifications")]
public NotiferSettings notiferSettings = new NotiferSettings();
[JsonProperty(Ru ? "Настройка радиации в зоне ивента" : "Setting up radiation in the event area")]
public RadiationConroller radiationConroller = new RadiationConroller();
[JsonProperty(Ru ? "Отображения ивента на картах" : "Event display on maps")]
public MapMarkers mapMarkers = new MapMarkers();
[JsonProperty(Ru ? "Настройка постройки для ивента (CopyPaste) и нпс" : "Setting up buildings for the event (Copypaste) and NPCs")]
public BuildingPasteSettings pasteSettings = new BuildingPasteSettings();
[JsonProperty(Ru ? "Настройка ящика" : "Customizing the box")]
public BoxSetting boxSetting = new BoxSetting();
[JsonProperty(Ru ? "Награда в виде команды, игроку который 1 открыл груз" : "Reward in the form of a team to the player who 1 opened the cargo")]
public CommandReward commandReward = new CommandReward();
[JsonProperty(Ru ? "Настройка подбора позиций для спавна (Для опытных пользователей)" : "Setting up the selection of positions for spawn (For experienced users)")]
public SpawnPositionGenerateSetting spawnPositionGenerateSetting = new SpawnPositionGenerateSetting();
internal class RadiationConroller {
[JsonProperty(Ru ? "Включить радиацию ?" : "Turn on radiation?")]
public bool radUse = true;
[JsonProperty(Ru ? "Количество радиационных частиц" : "Number of radiation particles")]
public int radCount = 20;
[JsonProperty(Ru ? "Радиус зоны поражения (Не более чем радиус обноружения игроков)" : "Radius of the affected area (No more than the radius of detection of players)")]
public int radRadius = 20;
}
internal class SpawnPositionGenerateSetting {
[JsonProperty(Ru ? "Разрешить спавн на дорогах ?" : "Allow spawn on the roads ?")]
public bool spawnOnRoad = true;
[JsonProperty(Ru ? "Разрешить спавн на реках ?" : "Allow spawn on rivers ?")]
public bool spawnOnRiver = true;
[JsonProperty(Ru ? "Разрешить спавн на берегах" : "Allow spawn on the beach")]
public bool spawnOnBeach = false;
[JsonProperty(Ru ? "Радиус обноружения монументов" : "Radius of monument detection")]
public float monumentFindRadius = 40f;
[JsonProperty(Ru ? "Радиус обноружения шкафов (Building Block)" : "Detection radius of the tool cupboard (Building Block)")]
public float buildingBlockFindRadius = 90f;
}
public class CommandReward {
[JsonProperty(Ru ? "Список команд, которые выполняются в консоли (%STEAMID% - игрок который залутает ящик )" : "List of commands that are executed in the console (%STEAMID% - the player who looted the box)")]
public List<string> Commands = new List<string>();
[JsonProperty(Ru ? "Сообщения который игрок получит (Здесь можно написать о том , что получил игрок)" : "Messages that the player will receive (Here you can write about what the player received)")]
public string MessagePlayerReward = "";
}
internal class MapMarkers {
[JsonProperty(Ru ? "Отметить ивент на карте G (Требуется https://umod.org/plugins/marker-manager)" : "Mark the event on the G card (Requires FREE https://umod.org/plugins/marker-manager)")]
public bool MapUse = true;
[JsonProperty(Ru ? "Текст для карты G" : "Text for map G")]
public string MapTxt = "Cobalt lab";
[JsonProperty(Ru ? "Цвет маркера (без #)" : "Marker color (without #)")]
public string colorMarker = "f3ecad";
[JsonProperty(Ru ? "Цвет обводки (без #)" : "Outline color (without #)")]
public string colorOutline = "ff3535";
}
internal class IventController {
[JsonProperty(Ru ? "Минимальное количество игроков для запуска ивента" : "The minimum number of players to start an event")]
public int minPlayedPlayers = 0;
[JsonProperty(Ru ? "Время до начала ивента (Минимальное в секундах)" : "Time before the start of the event (Minimum in seconds)")]
public int minSpawnIvent = 3000;
[JsonProperty(Ru ? "Время до начала ивента (Максимальное в секундах)" : "Time before the start of the event (Maximum in seconds)")]
public int maxSpawnIvent = 7200;
[JsonProperty(Ru ? "Время до удаления ивента если никто не откроет ящик (Секунды)" : "Time until the event is deleted if no one opens the box (Seconds)")]
public int timeRemoveHouse = 900;
[JsonProperty(Ru ? "Время до удаления ивента после того как разблокируется ящик" : "The time until the event is deleted after the box is unlocked")]
public int timeRemoveHouse2 = 300;
[JsonProperty(Ru ? "Создавать PVP зону в радиусе ивента ? (Требуется TruePVE)" : "Create a PVP zone within the radius of the event? (Requires TruePVE)")]
public bool UseZonePVP = false;
[JsonProperty(Ru ? "Используете ли вы купол" : "Do you use a dome ?")]
public bool useSphere = false;
[JsonProperty(Ru ? "Прозрачность купола (чем меньше число тем более он прозрачный.Значения должно быть не более 5)" : "Transparency of the dome (the smaller the number, the more transparent it is.The values should be no more than 5)")]
public int transperent = 3;
}
internal class BuildingPasteSettings {
[JsonProperty(Ru ? "Постройки для спавна.(Если более 1 то выбирается рандомная)" : "Spawn buildings.(If more than 1 then random is selected)")]
public List<PasteBuild> pasteBuilds = new List<PasteBuild>();
internal class PasteBuild {
[JsonProperty(Ru ? "Настройка высоты постройки (Требуется в настройке, если вы хотите ставить свою постройку)" : "Setting the height of the building (Required in the setting if you want to place your building)")]
public int heightBuilding;
[JsonProperty(Ru ? "Названия файла в папке `/oxide/data/copypaste` с вашей постройкой" : "The names of the file in the folder `/oxide/data/copypaste` with your building")]
public string housepath;
[JsonProperty(Ru ? "Шанс спавна коптера (Если присутствует в постройке)" : "Copter spawn chance (if present in a building)")]
public int copterChance;
[JsonProperty(Ru ? "Настройка NPC" : "NPCs setting")]
public HashSet<PresetConfig> NpcPressets;
}
public class PresetConfig {
[JsonProperty(Ru ? "Минимальное кол-во" : "Minimum")]
public int Min;
[JsonProperty(Ru ? "Максимальное кол-во" : "Maximum")]
public int Max;
[JsonProperty(Ru ? "Появятся ли они в качестве помощи после взлома запертого ящика?" : "Will they spawn as help after hacking locked crate?")]
public bool helpBot;
[JsonProperty(Ru ? "Колличевство нпс (Подмога)" : "Number of NPCs (Help)")]
public int helpCount;
[JsonProperty(Ru ? "Настройки NPC" : "NPCs setting")]
public NpcConfig Config;
[JsonProperty(Ru ? "Какую таблицу предметов необходимо использовать? (0 - стандартную; 1 - собственную; 2 - AlphaLoot;" : "Which loot table should the plugin use? (0 - default; 1 - own; 2 - AlphaLoot;")]
public int TypeLootTable;
[JsonProperty(Ru ? "Собственная таблица предметов (если тип таблицы предметов - 1)" : "Own loot table (if the loot table type is 1)")]
public LootTableConfig OwnLootTable = new LootTableConfig();
}
internal class NpcConfig {
[JsonProperty(Ru ? "Название" : "Name")]
public string Name;
[JsonProperty(Ru ? "Кол-во ХП" : "Health")]
public float Health;
[JsonProperty(Ru ? "Дальность патрулирования местности" : "Roam Range")]
public float RoamRange;
[JsonProperty(Ru ? "Дальность погони за целью" : "Chase Range")]
public float ChaseRange;
[JsonProperty(Ru ? "Множитель радиуса атаки" : "Attack Range Multiplier")]
public float AttackRangeMultiplier;
[JsonProperty(Ru ? "Радиус обнаружения цели" : "Sense Range")]
public float SenseRange;
[JsonProperty(Ru ? "Длительность памяти цели [sec.]" : "Target Memory Duration [sec.]")]
public float MemoryDuration;
[JsonProperty(Ru ? "Множитель урона" : "Scale damage")]
public float DamageScale;
[JsonProperty(Ru ? "Множитель разброса" : "Aim Cone Scale")]
public float AimConeScale;
[JsonProperty(Ru ? "Обнаруживать цель только в углу обзора NPC? [true/false]" : "Detect the target only in the NPC's viewing vision cone? [true/false]")]
public bool CheckVisionCone;
[JsonProperty(Ru ? "Угол обзора" : "Vision Cone")]
public float VisionCone;
[JsonProperty(Ru ? "Скорость" : "Speed")]
public float Speed;
[JsonProperty(Ru ? "Отключать эффекты рации? [true/false]" : "Disable radio effects? [true/false]")]
public bool DisableRadio;
[JsonProperty(Ru ? "Это стационарный NPC? [true/false]" : "Is this a stationary NPC? [true/false]")]
public bool Stationary;
[JsonProperty(Ru ? "Удалять труп после смерти? (рекомендуется использовать значение true для повышения производительности) [true/false]" : "Remove a corpse after death? (it is recommended to use the true value to improve performance) [true/false]")]
public bool IsRemoveCorpse;
[JsonProperty(Ru ? "Одежда" : "Wear items")]
public HashSet<NpcWear> WearItems;
[JsonProperty(Ru ? "Быстрые слоты" : "Belt items")]
public HashSet<NpcBelt> BeltItems;
[JsonProperty(Ru ? "Kit" : "Kit")]
public string Kit;
internal class NpcBelt {
[JsonProperty("ShortName")]
public string ShortName;
[JsonProperty(Ru ? "Кол-во" : "Amount")]
public int Amount;
[JsonProperty("SkinID (0 - default)")]
public ulong SkinID;
[JsonProperty(Ru ? "Модификации на оружие" : "Mods")]
public HashSet<string> Mods;
}
internal class NpcWear {
[JsonProperty("ShortName")]
public string ShortName;
[JsonProperty("SkinID (0 - default)")]
public ulong SkinID;
}
}
}
public class LootTableConfig {
[JsonProperty(Ru ? "Минимальное кол-во элементов" : "Minimum numbers of items")]
public int Min;
[JsonProperty(Ru ? "Максимальное кол-во элементов" : "Maximum numbers of items")]
public int Max;
[JsonProperty(Ru ? "Использовать минимальное и максимальное значение? [true/false]" : "Use minimum and maximum values? [true/false]")]
public bool UseCount;
[JsonProperty(Ru ? "Список предметов" : "List of items")]
public List<ItemConfig> Items = new List<ItemConfig>();
}
public class ItemConfig {
[JsonProperty("ShortName")]
public string ShortName;
[JsonProperty(Ru ? "Минимальное кол-во" : "Minimum")]
public int MinAmount;
[JsonProperty(Ru ? "Максимальное кол-во" : "Maximum")]
public int MaxAmount;
[JsonProperty(Ru ? "Шанс выпадения предмета [0.0-100.0]" : "Chance [0.0-100.0]")]
public float Chance;
[JsonProperty(Ru ? "Это чертеж? [true/false]" : "Is this a blueprint? [true/false]")]
public bool IsBluePrint;
[JsonProperty("SkinID (0 - default)")]
public ulong SkinID;
[JsonProperty(Ru ? "Название (empty - default)" : "Name (empty - default)")]
public string Name;
[JsonProperty(Ru ? "Умножать количество предмета на количество дней с начала вайпа (на 3й день - лута будет в 3 раза больше)" : "Multiply the amount of the item by the number of days since the beginning of the wipe (on the 3rd day, the loot will be 3 times more)")]
public bool lootWipePlus = false;
}
internal class BoxSetting {
[JsonProperty(Ru ? "Время разблокировки ящиков [sec.]" : "Time to unlock the Crates [sec.]")]
public float UnlockTime = 900f;
[JsonProperty(Ru ? "Какую таблицу лута необходимо использовать? (0 - стандартную; 1 - собственную; 2 - AlphaLoot; 3 - EcoLootUI; " : "Which loot table should the plugin use? (0 - default; 1 - own; 2 - AlphaLoot; 3 - EcoLootUI;")]
public int TypeLootTable = 0;
[JsonProperty(Ru ? "Собственная таблица предметов (если тип таблицы предметов - 1)" : "Own loot table (if the loot table type is 1)")]
public LootTableConfig OwnLootTable = new LootTableConfig();
[JsonProperty(Ru ? "Включить сигнализацию при взломе запертого язика ?" : "Turn on the alarm when breaking into a locked box ?")]
public bool signaling = true;
}
internal class NotiferSettings {
[JsonProperty(Ru ? "ВебХук дискорда (Если не нужны уведомления в дискорд, оставьте поле пустым)" : "Discord WebHook (If you do not need discord notifications, leave the field blank)")]
public string weebHook = string.Empty;
[JsonProperty(Ru ? "Включить UI Уведомления ?" : "Enable UI Notifications?")]
public bool useUiNotifi = true;
[JsonProperty(Ru ? "Скрывать автоматически UI уведомления?" : "Auto hide UI notifications?")]
public bool hideUiNotifi = true;
[JsonProperty(Ru ? "Через сколько после показа будет скрываться? (сек)" : "How long after the show will it hide? (sec)")]
public float hideUiNotifiTime = 15f;
[JsonProperty(Ru ? "Цвет заднего фона окна UI" : "UI window background color")]
public string colorBackground = "0.8 0.28 0.2 0.8";
[JsonProperty(Ru ? "Цвет Кнопки закрытия UI" : "UI Close Button Color")]
public string colorBtnCloseUi = "0.6784314 0.254902 0.1843137 0.8";
}
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) {
throw new Exception();
}
SaveConfig();
}
catch {
for (int i = 0; i < 3; i++) {
PrintError("Configuration file is corrupt! Check your config file at https://jsonlint.com/");
}
LoadDefaultConfig();
}
ValidateConfig();
SaveConfig();
}
private void ValidateConfig() {
if (config.pasteSettings.pasteBuilds.Count == 0) {
config.pasteSettings.pasteBuilds = new List<Configuration.BuildingPasteSettings.PasteBuild> {
new Configuration.BuildingPasteSettings.PasteBuild {
housepath = "HouseCobalt", heightBuilding = 0, copterChance = 50, NpcPressets = new HashSet<Configuration.BuildingPasteSettings.PresetConfig> {
new Configuration.BuildingPasteSettings.PresetConfig {
Min = 3, Max = 7, helpBot = true, helpCount = 3, Config = new Configuration.BuildingPasteSettings.NpcConfig {
Name = "Cobalt Defense", Health = 230f, RoamRange = 35f, ChaseRange = 10f, AttackRangeMultiplier = 3f, SenseRange = 50f, MemoryDuration = 40f, DamageScale = 2f, AimConeScale = 1f, CheckVisionCone = false, VisionCone = 135f, Speed = 8f, DisableRadio = false, Stationary = false, IsRemoveCorpse = false, WearItems = new HashSet<Configuration.BuildingPasteSettings.NpcConfig.NpcWear> {
new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "roadsign.kilt", SkinID = 1121447954
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "burlap.shirt", SkinID = 2076298726
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "shoes.boots", SkinID = 0
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "roadsign.gloves", SkinID = 0
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "burlap.trousers", SkinID = 2076292
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "metal.facemask", SkinID = 835028125
}
}
, BeltItems = new HashSet<Configuration.BuildingPasteSettings.NpcConfig.NpcBelt> {
new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "rifle.lr300", Amount = 1, SkinID = 1837473292, Mods = new HashSet<string> {
"weapon.mod.flashlight"
}
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "pistol.semiauto", Amount = 1, SkinID = 1557105240, Mods = new HashSet<string>()
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "syringe.medical", Amount = 10, SkinID = 0, Mods = new HashSet<string>()
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "grenade.smoke", Amount = 3, SkinID = 0, Mods = new HashSet<string>()
}
}
, Kit = ""
}
, TypeLootTable = 1, OwnLootTable = new Configuration.LootTableConfig {
Min = 1, Max = 1, UseCount = true, Items =new List<Configuration.ItemConfig> {
new Configuration.ItemConfig {
ShortName = "scrap", MinAmount = 5, MaxAmount = 10, Chance = 50f, IsBluePrint = false, SkinID = 0, Name = ""
}
, new Configuration.ItemConfig {
ShortName = "supply.signal", MinAmount = 1, MaxAmount = 1, Chance = 20f, IsBluePrint = false, SkinID = 0, Name = ""
}
, new Configuration.ItemConfig {
ShortName = "syringe.medical", MinAmount = 1, MaxAmount = 2, Chance = 70.0f, IsBluePrint = false, SkinID = 0, Name = ""
}
}
}
}
, new Configuration.BuildingPasteSettings.PresetConfig {
Min = 1, Max = 3, helpBot = false, helpCount = 0, Config = new Configuration.BuildingPasteSettings.NpcConfig {
Name = "Cobalt Guard", Health = 150f, RoamRange = 30.0f, ChaseRange = 90f, AttackRangeMultiplier = 2.0f, SenseRange = 50.0f, MemoryDuration = 30f, DamageScale = 1f, AimConeScale = 1f, CheckVisionCone = false, VisionCone = 135f, Speed = 7f, DisableRadio = true, Stationary = false, IsRemoveCorpse = false, WearItems = new HashSet<Configuration.BuildingPasteSettings.NpcConfig.NpcWear> {
new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "roadsign.kilt", SkinID = 1121447954
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "burlap.shirt", SkinID = 2076298726
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "shoes.boots", SkinID = 0
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "roadsign.gloves", SkinID = 0
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "burlap.trousers", SkinID = 2076292007
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcWear {
ShortName = "metal.facemask", SkinID = 835028125
}
}
, BeltItems = new HashSet<Configuration.BuildingPasteSettings.NpcConfig.NpcBelt> {
new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "rifle.lr300", Amount = 1, SkinID = 1837473292, Mods = new HashSet<string> {
"weapon.mod.flashlight"
}
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "pistol.semiauto", Amount = 1, SkinID = 1557105240, Mods = new HashSet<string>()
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "syringe.medical", Amount = 10, SkinID = 0, Mods = new HashSet<string>()
}
, new Configuration.BuildingPasteSettings.NpcConfig.NpcBelt {
ShortName = "grenade.f1", Amount = 3, SkinID = 0, Mods = new HashSet<string>()
}
}
, Kit = ""
}
, TypeLootTable = 1, OwnLootTable = new Configuration.LootTableConfig {
Min = 1, Max = 1, UseCount = true, Items =new List<Configuration.ItemConfig> {
new Configuration.ItemConfig {
ShortName = "scrap", MinAmount = 5, MaxAmount = 10, Chance = 50f, IsBluePrint = false, SkinID = 0, Name = ""
}
, new Configuration.ItemConfig {
ShortName = "supply.signal", MinAmount = 1, MaxAmount = 1, Chance = 20f, IsBluePrint = false, SkinID = 0, Name = ""
}
, new Configuration.ItemConfig {
ShortName = "syringe.medical", MinAmount = 1, MaxAmount = 2, Chance = 70.0f, IsBluePrint = false, SkinID = 0, Name = ""
}
}
}
}
}
}
}
;
}
if (config.boxSetting.OwnLootTable.Items.Count == 0) {
config.boxSetting.OwnLootTable.Items = new List<Configuration.ItemConfig> {
new Configuration.ItemConfig {
ShortName = "pistol.python", SkinID = 0, Name = "", IsBluePrint = false, MinAmount = 1, MaxAmount = 1, Chance = 60,
}
, new Configuration.ItemConfig {
ShortName = "multiplegrenadelauncher", SkinID = 0, Name = "", IsBluePrint = false, MinAmount = 1, MaxAmount = 1, Chance = 15,
}
, new Configuration.ItemConfig {
ShortName = "sulfur", SkinID = 0, Name = "", IsBluePrint = false, MinAmount = 500, MaxAmount = 800, Chance = 40,
}
, new Configuration.ItemConfig {
ShortName = "wall.external.high.ice", SkinID = 0, Name = "", IsBluePrint = false, MinAmount = 1, MaxAmount = 5, Chance = 75,
}
,
}
;
}
}
protected override void SaveConfig() {
Config.WriteObject(config);
}
protected override void LoadDefaultConfig() {
config = new Configuration();
}
#endregion
#region SpawnPoint
#region CheckFlat
private List<Vector3>[] patternPositionsAboveWater = new List<Vector3>[MaxRadius];
private List<Vector3>[] patternPositionsUnderWater = new List<Vector3>[MaxRadius];
private const float MaxElevation = 2.0f;
private readonly Quaternion[] directions = {
Quaternion.Euler(90, 0, 0), Quaternion.Euler(0, 0, 90), Quaternion.Euler(0, 0, 180)
}
;
private void FillPatterns() {
Vector3[] startPositions = {
new Vector3(1, 0, 1), new Vector3(-1, 0, 1), new Vector3(-1, 0, -1), new Vector3(1, 0, -1)
}
;
patternPositionsAboveWater[0] = new List<Vector3> {
new Vector3(0, MaxElevation, 0)
}
;
for (int loop = 1; loop < MaxRadius; loop++) {
patternPositionsAboveWater[loop] = new List<Vector3>();
for (int step = 0; step < loop * 2; step++) {
for (int pos = 0; pos < 4; pos++) {
Vector3 sPos = startPositions[pos] * step;
for (int rot = 0; rot < 3; rot++) {
Vector3 rPos = directions[rot] * sPos;
rPos.y = -MaxElevation;
patternPositionsAboveWater[loop].Add(rPos);
}
}
}
}
for (int i = 0; i < patternPositionsAboveWater.Length; i++) {
patternPositionsUnderWater[i] = new List<Vector3>();
foreach (Vector3 vPos in patternPositionsAboveWater[i]) {
Vector3 rPos = new Vector3(vPos.x, MaxElevation, vPos.z);
patternPositionsUnderWater[i].Add(rPos);
}
}
}
private bool IsFlat(ref Vector3 position) {
List<Vector3>[] AboveWater = new List<Vector3>[MaxRadius];
Array.Copy(patternPositionsAboveWater, AboveWater, patternPositionsAboveWater.Length);
for (int i = 0; i < AboveWater.Length; i++) {
for (int j = 0; j < AboveWater[i].Count; j++) {
Vector3 pPos = AboveWater[i][j];
Vector3 resultAbovePos = new Vector3(pPos.x + position.x, position.y + MaxElevation, pPos.z + position.z);
Vector3 resultUnderPos = new Vector3(pPos.x + position.x, position.y - MaxElevation, pPos.z + position.z);
if (resultAbovePos.y >= TerrainMeta.HeightMap.GetHeight(resultAbovePos) && resultUnderPos.y <= TerrainMeta.HeightMap.GetHeight(resultUnderPos)) {
}
else {
return false;
}
}
}
return true;
}
#endregion
#region GenerateSpawnPoint
private Coroutine FindPositions;
private void PosValidation(Vector3 pos) {
if (!IsFlat(ref pos)) {
return;
}
if (WaterLevel.GetWaterDepth(pos, false, false) > 0.5f) {
return;
}
if (!Is3DPointValid(pos, 1 << 8 | 1 << 16 | 1 << 18)) {
return;
}
if (!config.spawnPositionGenerateSetting.spawnOnRiver && ContainsTopology(TerrainTopology.Enum.Riverside | TerrainTopology.Enum.River, pos, 12.5f)) {
return;
}
if (!config.spawnPositionGenerateSetting.spawnOnBeach && ContainsTopology(TerrainTopology.Enum.Beach | TerrainTopology.Enum.Beachside | TerrainTopology.Enum.Ocean | TerrainTopology.Enum.Oceanside, pos, 20f)) {
return;
}
if (!config.spawnPositionGenerateSetting.spawnOnRoad && ContainsTopology(TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside, pos, 12.5f)) {
return;
}
if (ContainsTopology(TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside, pos, 12.5f)) {
return;
}
if (ContainsTopology(TerrainTopology.Enum.Monument, pos, config.spawnPositionGenerateSetting.monumentFindRadius)) {
return;
}
if (ContainsTopology(TerrainTopology.Enum.Building, pos, 25f)) {
return;
}
if (pos != Vector3.zero) {
AcceptValue(ref pos);
}
}
private IEnumerator GenerateSpawnPoints() {
int minPos = (int)(World.Size / -2f);
int maxPos = (int)(World.Size / 2f);
int checks = 0;
for (float x = minPos; x < maxPos; x += 20f) {
for (float z = minPos; z < maxPos; z += 20f) {
Vector3 pos = new Vector3(x, 0f, z);
pos.y = GetSpawnHeight(pos);
PosValidation(pos);
if (++checks >= 75) {
checks = 0;
yield return CoroutineEx.waitForSeconds(0.05f);
}
}
}
ServerMgr.Instance.StopCoroutine(FindPositions);
FindPositions = null;
}
private bool IsAsset(string value) {
foreach (string asset in assets) {
if (value.Contains(asset)) {
return true;
}
}
return false;
}
private readonly List<string> assets = new List<string> {
"/props/", "/structures/", "/building/", "train_", "powerline_", "dune", "candy-cane", "assets/content/nature/", "walkway", "invisible_collider"
}
;
private float GetRockHeight(Vector3 a) {
RaycastHit hit;
if (Physics.Raycast(a + new Vector3(0f, 50f, 0f), Vector3.down, out hit, a.y + 51f, Layers.Mask.World, QueryTriggerInteraction.Ignore)) {
return Mathf.Abs(hit.point.y - a.y);
}
return 0f;
}
private bool Is3DPointValid(Vector3 point, int layer) {
List<Collider> colliders = Pool.Get<List<Collider>>();
Vis.Colliders(point, 25f, colliders, layer, QueryTriggerInteraction.Collide);
int count = colliders.Count;
foreach (Collider collider in colliders) {
if (collider == null || collider.transform == null) {
count--;
continue;
}
if (collider.name.Contains("SafeZone")) {
count = int.MaxValue;
continue;
}
BaseEntity e = collider.ToBaseEntity();
if (IsAsset(collider.name) && (e == null || e.name.Contains("/treessource/"))) {
count = int.MaxValue;
break;
}
if (e.IsValid()) {
if (e is BasePlayer) {
BasePlayer player = e as BasePlayer;
if (player.IsSleeping()) {
count--;
}
else if (player.IsNpc || player.IsFlying) {
// ... [Code preview truncated due to length. Full source in attached file] ...