C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Facepunch.Utility;
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 UnityEngine;
using UnityEngine.Networking;
using Random = Oxide.Core.Random;
namespace Oxide.Plugins {
[Info("XDQuest", "SkuliDropek", "6.0.8")]
[Description("Расширенная квест система для вашего сервера!")]
public class XDQuest : RustPlugin {
/// <summary> //6.0.8
/// - Исправлен API для Raidable Bases (Nivex)
/// - Убрано новогодние оформление
/// - Добавлена возможность очищать прогресс при вайпе.По стандарту включено в конфигурации
/// </summary>
#region ReferencePlugins
[PluginReference]
Plugin CopyPaste, ImageLibrary, IQChat, Friends, Clans, Battles, Duel, Notify;
private void SendChat(BasePlayer player, string Message, ConVar.Chat.ChatChannel channel = ConVar.Chat.ChatChannel.Global) {
if (IQChat) IQChat?.Call("API_ALERT_PLAYER", player, Message, config.settingsIQChat.prifix, config.settingsIQChat.SteamID);
else player.SendConsoleCommand("chat.add", channel, 0, Message);
}
private bool IsFriends(ulong userID, ulong targetID) {
if (Friends) return (bool)Friends?.Call("HasFriend", userID, targetID);
else return false;
}
private bool IsClans(string userID, string targetID) {
if (Clans) {
String TagUserID = (String)Clans?.Call("GetClanOf", userID);
String TagTargetID = (String)Clans?.Call("GetClanOf", targetID);
if (TagUserID == null && TagTargetID == null) return false;
return (bool)(TagUserID == TagTargetID);
}
else return false;
}
private bool IsDuel(ulong userID) {
if (Battles) return (bool)Battles?.Call("IsPlayerOnBattle", userID);
else if (Duel) return (bool)Duel?.Call("IsPlayerOnActiveDuel", BasePlayer.FindByID(userID));
else return false;
}
private string GetImage(string shortname, ulong skin = 0) => (string)ImageLibrary?.Call("GetImage", shortname, skin);
private void SendImage(BasePlayer player, string imageName, ulong imageId = 0) => ImageLibrary?.Call("SendImage", player, imageName, imageId);
#endregion
#region Variables
private static XDQuest Instance;
private static readonly String Key = "OZh9xW02owxY1dd";
private MonumentInfo monument;
private BasePlayer npc;
private ComputerStation chairNpc;
private List<BaseEntity> HouseNPC = new List<BaseEntity>();
private static List<uint> Light = new List<uint> {
1392608348, 110576239, 3341019015, 1797934483, 2409469892, 3887352222, 3953213470
}
;
private List<Quest> QuestList = new List<Quest>();
private SafeZone safeZone = null;
private ZoneTrigger zoneTrigger = null;
private Dictionary<ulong, PlayerData> playersInfo = new Dictionary<ulong, PlayerData>();
private class PlayerData {
public List<string> PlayerQuestsFinish = new List<string>();
public List<PlayerQuest> PlayerQuestsAll = new List<PlayerQuest>();
public Dictionary<string, double> PlayerQuestsCooldown = new Dictionary<string, double>();
}
#endregion
#region Lang
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["XDQUEST_CopyPasteError"]
= "There was a problem with CopyPaste! Contact the Developer!\nDezLife#1480\nnvk.com/dezlife", ["XDQUEST_CopyPasteSuccessfully"] = "The building has spawned successfully!", ["XDQUEST_BuildingPasteError"] = "There was a problem with spawning the Building! Contact the Developer!\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_MissingOutPost"] = "Your map doesnt have an Outpost Monument.Please use a custom spawn point.", ["XDQUEST_MissingQuests"] = "You do not have a file with tasks, the plugin will not work correctly! Create one on the Website - https://xdquest.skyplugins.ru/ or use the included one.", ["XDQUEST_BuildingLoad"] = "Initializing building for NPC...", ["XDQUEST_KeyAuth"] = "The plugin did not pass the authentication on the server!\nCheck the plugin version or contact the developer\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_FileNotFoundOnServer"] = "File {0} could not be found on server.Contact the developer\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_ServerNotResponse"] = "Unable to load the file {0}, Server response: {1}.Retrying to download...", ["XDQUEST_FileNotLoad"] = "Downloading the file {0} was unsuccessful.Contact the developer\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_UI_TASKLIST"] = "Quest List", ["XDQUEST_UI_Awards"] = "Rewards", ["XDQUEST_UI_TASKCount"] = "<color=#42a1f5>{0}</color> QUESTS", ["XDQUEST_UI_CHIPperformed"] = "Completed", ["XDQUEST_UI_CHIPInProgress"] = "In progress", ["XDQUEST_UI_QUESTREPEATCAN"] = "Yes", ["XDQUEST_UI_QUESTREPEATfForbidden"] = "No", ["XDQUEST_UI_Missing"] = "Missing", ["XDQUEST_UI_InfoRepeatInCD"] = "Repeat {0} | Cooldown {1} | Hand in {2}", ["XDQUEST_UI_QuestNecessary"] = "Needed", ["XDQUEST_UI_QuestNotNecessary"] = "Not needed", ["XDQUEST_UI_QuestBtnPerformed"] = "COMPLETED", ["XDQUEST_UI_QuestBtnTake"] = "TAKE", ["XDQUEST_UI_QuestBtnPass"] = "COMPLETE", ["XDQUEST_UI_QuestBtnRefuse"] = "REFUSE", ["XDQUEST_UI_ACTIVEOBJECTIVES"] = "Objective: {0}", ["XDQUEST_UI_MiniQLInfo"] = "{0}\nProgress: {1} / {2}\nQuest: {3}", ["XDQUEST_UI_CMDCustomPosAdd"] = "You have successfully added a custom building position.\n(You need to reload the plugin)\nYou can rotate the building in the config!\nRemember to enable the option to spawn a building on a custom position in the config.", ["XDQUEST_UI_QuestLimit"] = "You have to many <color=#4286f4>unfinished</color> Quests", ["XDQUEST_UI_AlreadyTaken"] = "You have already <color=#4286f4>taken</color> this Quest!", ["XDQUEST_UI_AlreadyDone"] = "You have already <color=#4286f4>completed</color> this Quest!", ["XDQUEST_UI_TookTasks"] = "You have <color=#4286f4>successfully</color> accepted the Quest {0}", ["XDQUEST_UI_ACTIVECOLDOWN"] = "This Quest is on Cooldown.", ["XDQUEST_UI_LackOfSpace"] = "Your inventory is full! Clear some space and try again!", ["XDQUEST_UI_QuestsCompleted"] = "Quest Completed! Enjoy your reward!", ["XDQUEST_UI_PassedTasks"] = "So this Quest was to much for you? \n Try again later!", ["XDQUEST_UI_ActiveQuestCount"] = "You have no active Quests.", ["XDQUEST_Finished_QUEST"] = "You have completed the task: <color=#4286f4>{0}</color>", ["XDQUEST_UI_InsufficientResources"] = "You don't have {0}, you should definitely bring this to Sidorovich", ["XDQUEST_UI_NotResourcesAmount"] = "You don't have enough {0}, you need {1}", ["XDQUEST_SoundLoadErrorExt"] = "The voice file {0} is missing, upload it using this path - (/data/XDQuest/Sounds).Or remove it from the configuration"
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
["XDQUEST_CopyPasteError"]
= "Возникла проблема с CopyPaste! Обратитесь к разработчику\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_CopyPasteSuccessfully"] = "Постройка успешно заспавнена!", ["XDQUEST_BuildingPasteError"] = "Ошибка спавна поостройки! Обратитесь к разработчику\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_MissingOutPost"] = "У вас отсутствует outpost, вы можете использовать кастомную позицию для постройки", ["XDQUEST_MissingQuests"] = "У вас отсутсвует файл с заданиями, плагин будет работать не коректно! Создайте его на сайте - https://xdquest.skyplugins.ru/ или используйте стандартный", ["XDQUEST_BuildingLoad"] = "Инициализация постройки для NPC...", ["XDQUEST_KeyAuth"] = "Плагин не смог пройти аунтефикацию на сервере!\n Сверьте версию плагина или свяжитесь с разработчиком\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_FileNotFoundOnServer"] = "Файл {0}, не найден на сервере.Обратитесь к разработчику\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_ServerNotResponse"] = "Невозможно загрузить файл {0}, Ответ сервера: {1}.Пробуем повторить загрузку...", ["XDQUEST_FileNotLoad"] = "Повторная загрузка файла {0}, не увенчалась успехом.Обратитесь к разработчику\nDezLife#1480\nvk.com/dezlife", ["XDQUEST_UI_TASKLIST"] = "СПИСОК ЗАДАНИЙ", ["XDQUEST_UI_Awards"] = "Награды", ["XDQUEST_UI_TASKCount"] = "<color=#42a1f5>{0}</color> ЗАДАНИЙ", ["XDQUEST_UI_CHIPperformed"] = "выполнено", ["XDQUEST_UI_CHIPInProgress"] = "выполняется", ["XDQUEST_UI_QUESTREPEATCAN"] = "можно", ["XDQUEST_UI_QUESTREPEATfForbidden"] = "нельзя", ["XDQUEST_UI_Missing"] = "отсутствует", ["XDQUEST_UI_InfoRepeatInCD"] = "Повторно брать {0} | Кд на повторное взятие {1} | Сдать добытое {2}", ["XDQUEST_UI_QuestNecessary"] = "нужно", ["XDQUEST_UI_QuestNotNecessary"] = "не нужно", ["XDQUEST_UI_QuestBtnPerformed"] = "ВЫПОЛНЕНО", ["XDQUEST_UI_QuestBtnTake"] = "ВЗЯТЬ", ["XDQUEST_UI_QuestBtnPass"] = "ЗАВЕРШИТЬ", ["XDQUEST_UI_QuestBtnRefuse"] = "ОТКАЗАТЬСЯ", ["XDQUEST_UI_ACTIVEOBJECTIVES"] = "АКТИВНЫЕ ЗАДАЧИ: {0}", ["XDQUEST_UI_MiniQLInfo"] = "{0}\nПрогресс: {1} / {2}\nЗадача: {3}", ["XDQUEST_UI_CMDCustomPosAdd"] = "Вы успешно добавили кастомную позицию для постройки.\n(Вам нужно перезагрузить плагин)\nПовернуть ее можно в конфиге!\nТак же не забудъте включить в конфиге возможность спавнить постройку на кастомной позиции", ["XDQUEST_UI_QuestLimit"] = "У тебя слишком много <color=#4286f4>не законченных</color> заданий!", ["XDQUEST_UI_AlreadyTaken"] = "Вы уже <color=#4286f4>взяли</color> это задание!", ["XDQUEST_UI_AlreadyDone"] = "Вы уже <color=#4286f4>выполняли</color> это задание!", ["XDQUEST_UI_TookTasks"] = "Вы <color=#4286f4>успешно</color> взяли задание {0}", ["XDQUEST_UI_ACTIVECOLDOWN"] = "В данный момент вы не можете взять этот квест", ["XDQUEST_UI_LackOfSpace"] = "Эй, погоди, ты всё <color=#4286f4>не унесёшь</color>, освободи место!", ["XDQUEST_UI_QuestsCompleted"] = "Спасибо, держи свою <color=#4286f4>награду</color>!", ["XDQUEST_UI_PassedTasks"] = "Жаль что ты <color=#4286f4>не справился</color> с заданием!\nВ любом случае, ты можешь попробовать ещё раз!", ["XDQUEST_UI_ActiveQuestCount"] = "У вас нет активных заданий.", ["XDQUEST_Finished_QUEST"] = "Вы выполнили задание: <color=#4286f4>{0}</color>", ["XDQUEST_UI_InsufficientResources"] = "У вас нету {0}, нужно обязательно принести это сидоровичу", ["XDQUEST_UI_NotResourcesAmount"] = "У вас не достаточно {0}, нужно {1}", ["XDQUEST_SoundLoadErrorExt"] = "Отсутсвует голосовой файл {0}, загрузите его по этому пути - (/data/XDQuest/Sounds).Или удалите его из конфигурации"
}
, this, "ru");
}
#endregion
#region Configuration
private Configuration config;
private class Configuration {
public class itemsNpc {
[JsonProperty("ShortName")]
public String ShortName;
[JsonProperty("SkinId")]
public ulong SkinId;
}
public class Settings {
/*3*/
[JsonProperty("Максимальное колличевство единовременно взятых квестов")]
////[JsonProperty("The maximum number of simultaneously taken quests")]
public Int32 questCount = 3;
/*3*/
[JsonProperty("Голосовое оповещение при выполнении задания")]
////[JsonProperty("Voice notification when completing a task")]
public Boolean SoundEffect = true;
/*3*/
[JsonProperty("Включите этот параметр если у вас гниет постройка")]
////[JsonProperty("Enable this option if your building is rotting")]
public Boolean useDecay = false;
/*3*/
[JsonProperty("Отчищать прогресс игроков при вайпе ?")]
////[JsonProperty("Clear player progress when wipe ?")]
public Boolean useWipe = true;
/*3*/
[JsonProperty("Эфект")]
////[JsonProperty("Effect")]
public String Effect = "assets/prefabs/locks/keypad/effects/lock.code.lock.prefab";
/*3*/
[JsonProperty("Names of the file with quests")]
////[JsonProperty("Names of the file with quests")]
public String questListDataName = "Quest";
/*3*/
[JsonProperty("Команда для открытия квест листа с прогрессом")]
////[JsonProperty("The command to open a quest list with progress")]
public String questListProgress = "qlist";
/*3*/
[JsonProperty("Радиус безопасной зоны (Как в городе нпс)")]
////[JsonProperty("Safe Zone Radius (As in NPC City)")]
public float saveZoneRadius = 25;
/*3*/
[JsonProperty("Включить ли радио у нпс в здании ?")]
////[JsonProperty("Should the radio be turned on for the NPCs in the building?")]
public Boolean useRadio = true;
/*3*/
[JsonProperty("Ссылка на радио станцию которая будет играть в доме")]
////[JsonProperty("Link to the radio station that will play in the house")]
public String RadioStation = "http://radio.skyplugins.ru:8020/stalker.mp3";
/*3*/
[JsonProperty("Использовать метку на внутриигровой карте ? (Требуется https://skyplugins.ru/resources/428/)")]
////[JsonProperty("Use a placemark on an in-game map ? (Required https://skyplugins.ru/resources/428/)")]
public Boolean mapUse = false;
/*3*/
[JsonProperty("Имя метки на карте")]
////[JsonProperty("Name of the placemark on the map")]
public String nameMarkerMap = "QUEST ROOM";
/*3*/
[JsonProperty("Цвет маркера (без #)")]
////[JsonProperty("Marker color (without #)")]
public String colorMarker = "f3ecad";
/*3*/
[JsonProperty("Цвет обводки (без #)")]
////[JsonProperty("Outline color (without #)")]
public String colorOutline = "ff3535";
}
public class CustomPosition {
/*3*/
[JsonProperty("Использовать кастомную позицию постройки ?")]
////[JsonProperty("Use a custom construction position ?")]
public Boolean useCustomPos = false;
/*3*/
[JsonProperty("Позиция постройки")]
////[JsonProperty("Construction position")]
public Vector3 pos = Vector3.zero;
/*3*/
[JsonProperty("Поворот постройки (Этим параметром вы можете повернуть постройку)")]
////[JsonProperty("Rotate the building (You can use this parameter to rotate the building)")]
public Int32 rotation = 0;
}
public class SettingsNpc {
/*3*/
[JsonProperty("Имя нпс")]
////[JsonProperty("NPC name")]
public String Name = "Сидорович\n";
///Sidorovich/Сидорович
/*3*/
[JsonProperty("id npc (От его ид зависит его внешность)")]
////[JsonProperty("npc id (His appearance depends on his ID)")]
public ulong userId = 21;
/*3*/
[JsonProperty("Одежда нпс")]
////[JsonProperty("NPC Clothing")]
public List<itemsNpc> Wear = new List<itemsNpc>();
}
public class SettingsIQChat {
/*3*/
[JsonProperty("Префикс в чате")]
////[JsonProperty("Prefix in the chat")]
public String prifix = "Sidorovich:";
/*3*/
[JsonProperty("SteamID - Для аватарки из профиля стим")]
////[JsonProperty("SteamID - For the avatar from the steam profile")]
public String SteamID = "21";
}
public class SettingsNotify {
/*3*/
[JsonProperty("Включить уведомления (Требуется - https://codefling.com/plugins/notify)")]
////[JsonProperty("Enable notifications (Is required - https://codefling.com/plugins/notify)")]
public bool useNotify = false;
/*3*/
[JsonProperty("Тип уведомления (Требуется - https://codefling.com/plugins/notify)")]
////[JsonProperty("Notification Type (Is required - https://codefling.com/plugins/notify)")]
public int typeNotify = 0;
}
public class SettingsSoundNPC {
/*3*/
[JsonProperty("Включить возможность разговаривать NPC")]
////[JsonProperty("Enable the ability to talk to NPCs")]
public bool soundUse = true;
/*3*/
[JsonProperty("Заполнять стандартными звуками ? (Нужно добавить звуки в дату!)")]
////[JsonProperty("Fill it with standard sounds ? (Need to add sounds to the date!)")]
public bool soundAddToCfg = true;
/*3*/
[JsonProperty("Название файлов со звуком для приветствия")]
////[JsonProperty("The name of the files with the greeting sound")]
public List<string> heySound = new List<string>();
/*3*/
[JsonProperty("Название файлов со звуком для прощание")]
////[JsonProperty("The name of the files with the sound for farewell")]
public List<string> byeSound = new List<string>();
/*3*/
[JsonProperty("Название файлов со звуком для взятие задания")]
////[JsonProperty("The name of the files with the sound for taking the task")]
public List<string> takeQuestSound = new List<string>();
/*3*/
[JsonProperty("Название файлов со звуком для сдачи задания")]
////[JsonProperty("The name of the files with the sound to complete the task")]
public List<string> turnQuestSound = new List<string>();
}
/*3*/
[JsonProperty("Настройка кастомной позиции постройки")]
////[JsonProperty("Setting up a custom building position")]
public CustomPosition customPosition = new CustomPosition();
/*3*/
[JsonProperty("Настройки NPC")]
////[JsonProperty("NPC Settings")]
public SettingsNpc settingsNpc = new SettingsNpc();
/*3*/
[JsonProperty("Настройки")]
////[JsonProperty("Settings")]
public Settings settings = new Settings();
/*3*/
[JsonProperty("Настройки звуков/разговоров NPC")]
////[JsonProperty("NPC Sound/Conversation Settings")]
public SettingsSoundNPC settingsSoundNPC = new SettingsSoundNPC();
/*3*/
[JsonProperty("Настройки IQChat (Если есть)")]
////[JsonProperty("Ichat Settings (If any)")]
public SettingsIQChat settingsIQChat = new SettingsIQChat();
/*3*/
[JsonProperty("Настройки уведомления")]
////[JsonProperty("Notification Settings")]
public SettingsNotify settingsNotify = new SettingsNotify();
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) throw new Exception();
SaveConfig();
}
catch {
for (var 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.settingsNpc.Wear.Count == 0) {
config.settingsNpc.Wear = new List<Configuration.itemsNpc> {
new Configuration.itemsNpc {
ShortName = "pants", SkinId = 960252273,
}
, new Configuration.itemsNpc {
ShortName = "hoodie", SkinId = 959641236,
}
, new Configuration.itemsNpc {
ShortName = "shoes.boots", SkinId = 962503020,
}
}
;
}
#region RU
if (config.settingsSoundNPC.soundAddToCfg) {
if (config.settingsSoundNPC.heySound.Count == 0) {
config.settingsSoundNPC.heySound = new List<string> {
"hello_1", "hello_2"
}
;
}
if (config.settingsSoundNPC.byeSound.Count == 0) {
config.settingsSoundNPC.byeSound = new List<string> {
"bye_1", "bye_2"
}
;
}
if (config.settingsSoundNPC.takeQuestSound.Count == 0) {
config.settingsSoundNPC.takeQuestSound = new List<string> {
"pick_up_quest_1", "pick_up_quest_2"
}
;
}
if (config.settingsSoundNPC.turnQuestSound.Count == 0) {
config.settingsSoundNPC.turnQuestSound = new List<string> {
"turn_in_quest_1", "turn_in_quest_2"
}
;
}
config.settingsSoundNPC.soundAddToCfg = false;
}
#endregion
#region EN
//if (config.settingsSoundNPC.soundAddToCfg)
//{
// if (config.settingsSoundNPC.heySound.Count == 0)
// {
// config.settingsSoundNPC.heySound = new List<string>
// {
// "hello_1", "hello_2", "hello_3"
// };
// }
// if (config.settingsSoundNPC.byeSound.Count == 0)
// {
// config.settingsSoundNPC.byeSound = new List<string>
// {
// "bye_1", "bye_2", "bye_3"
// };
// }
// if (config.settingsSoundNPC.takeQuestSound.Count == 0)
// {
// config.settingsSoundNPC.takeQuestSound = new List<string>
// {
// "pick_up_quest"
// };
// }
// if (config.settingsSoundNPC.turnQuestSound.Count == 0)
// {
// config.settingsSoundNPC.turnQuestSound = new List<string>
// {
// "turn_in_quest", "turn_in_quest_2"
// };
// }
// config.settingsSoundNPC.soundAddToCfg = false;
//}
#endregion
}
protected override void SaveConfig() {
Config.WriteObject(config);
}
protected override void LoadDefaultConfig() {
config = new Configuration();
}
#endregion
#region QuestData
private class PlayerQuest {
public Quest parentQuest;
public ulong UserID;
public bool Finished;
public int Count;
public void AddCount(int amount = 1) {
Count += amount;
BasePlayer player = BasePlayer.FindByID(UserID);
if (parentQuest.Amount <= Count) {
Count = parentQuest.Amount;
if (player != null && player.IsConnected) {
if (Instance.config.settings.SoundEffect) Instance.RunEffect(player, Instance.config.settings.Effect);
if (Instance.config.settingsNotify.useNotify && Instance.Notify) Instance.Notify.CallHook("SendNotify", player, Instance.config.settingsNotify.typeNotify, Instance.GetLang("XDQUEST_Finished_QUEST", player.UserIDString, parentQuest.DisplayName));
else Instance.SendChat(player, Instance.GetLang("XDQUEST_Finished_QUEST", player.UserIDString, parentQuest.DisplayName));
Interface.CallHook("OnQuestCompleted", player, parentQuest.DisplayName);
}
Finished = true;
}
if (Instance.openQuestPlayers.Contains(UserID)) {
CuiHelper.DestroyUi(player, MiniQuestList);
Instance.OpenMQL_CMD(player);
}
}
public int LeftAmount() => parentQuest.Amount - Count;
}
public enum QuestType {
IQPlagueSkill, IQHeadReward, IQCases, OreBonus, XDChinookIvent, Gather, EntityKill, Craft, Research, Loot, Grade, Swipe, Deploy, PurchaseFromNpc, HackCrate, RecycleItem, Growseedlings, RaidableBases, Fishing
}
public enum PrizeType {
Item, BluePrint, CustomItem, Command
}
private class Quest {
internal class Prize {
public string nameprize;
public PrizeType type;
public string ShortName;
public int Amount;
public string Name;
public ulong SkinID;
public string Command;
public string Url;
}
public string DisplayName;
public string Description;
public string Missions;
public QuestType QuestType;
public string Target;
public int Amount;
public bool UseRepeat;
public bool Bring;
public int Cooldown;
public List<Prize> PrizeList = new List<Prize>();
}
#endregion
#region MetodsBuildingAndNpc
private class Building {
public string name;
public float Deg2Rad;
public Vector3 pos;
}
private Dictionary<string, Building> BuildingList = new Dictionary<string, Building> {
["1"]
= new Building {
name = "QuestHouseChristmas", Deg2Rad = 4.75f, pos = new Vector3(-7.32f, 1.76f, 43.83f)
}
, ["2"] = new Building {
name = "QuestHouse6", Deg2Rad = 5.38f, pos = new Vector3(-6.76f, 1.73f, 46.63f)
}
,
}
;
void GenerateBuilding() {
var options = new List<string> {
"stability", "true", "deployables", "true", "autoheight", "false", "entityowner", "true"
}
;
Vector3 resultVector = GetResultVector();
var success = CopyPaste.Call("TryPasteFromVector3", resultVector, config.customPosition.useCustomPos ? (Vector3.zero * Mathf.Deg2Rad).y - config.customPosition.rotation : (monument.transform.rotation.eulerAngles * Mathf.Deg2Rad).y - BuildingList["2"].Deg2Rad, BuildingList["2"].name, options.ToArray());
if (success is string) {
PrintWarning(GetLang("XDQUEST_CopyPasteError"));
return;
}
if (config.settings.mapUse) Interface.CallHook("API_CreateMarker", resultVector, "xdquest", 0, 3f, 0.2f, config.settings.nameMarkerMap, config.settings.colorMarker, config.settings.colorOutline);
if (config.customPosition.useCustomPos) {
safeZone = new GameObject().AddComponent<SafeZone>();
safeZone.Activate(resultVector, config.settings.saveZoneRadius);
}
}
private void CrateBox(Vector3 pos) {
BaseEntity box = GameManager.server.CreateEntity("assets/prefabs/deployable/quarry/fuelstorage.prefab", pos + Vector3.up, Quaternion.identity);
box.enableSaving = false;
box.OwnerID = 76561198283599982;
box.skinID = 1195832261;
box.Spawn();
HouseNPC.Add(box);
}
private void InitializeNPC(Vector3 pos) {
npc = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", pos) as BasePlayer;
if (npc == null) {
Interface.Oxide.LogError($"Initializing NPC failed! NPC Component == null #3");
return;
}
npc.userID = config.settingsNpc.userId;
npc.name = config.settingsNpc.Name;
npc.displayName = npc.name;
npc.Spawn();
npc.SendNetworkUpdate();
chairNpc.MountPlayer(npc);
chairNpc.SetFlag(BaseEntity.Flags.Busy, true);
CrateBox(npc.transform.position);
if (config.settingsSoundNPC.soundUse) {
zoneTrigger = new GameObject().AddComponent<ZoneTrigger>();
zoneTrigger.Activate(pos, 3.2f);
}
#region NpcWearStart
if (config.settingsNpc.Wear.Count > 0) for (int i = 0; i < config.settingsNpc.Wear.Count; i++) ItemManager.Create(ItemManager.FindItemDefinition(config.settingsNpc.Wear[i].ShortName), 1, config.settingsNpc.Wear[i].SkinId).MoveToContainer(npc.inventory.containerWear);
#endregion
}
private void ClearEnt() {
IEnumerable<BasePlayer> findplayer = FindMyBot(config.settingsNpc.userId);
foreach (var player in findplayer) if (player != null) player.KillMessage();
List<BaseEntity> obj = new List<BaseEntity>();
Vis.Entities(GetResultVector(), 10f, obj, LayerMask.GetMask("Construction", "Deployable", "Deployed", "Debris", "Default"));
foreach (BaseEntity item in obj.Where(x => x.OwnerID == 76561198283599982)) {
if (item == null) continue;
item.Kill();
}
timer.Once(5f, () => {
GenerateBuilding();
}
);
}
void OnPasteFinished(List<BaseEntity> pastedEntities, string fileName) {
if (fileName.ToLower() != BuildingList["2"].name.ToLower()) return;
try {
HouseNPC.AddRange(pastedEntities);
foreach (BaseEntity item in HouseNPC) {
if (Light.Contains(item.prefabID)) {
item.enableSaving = true;
item?.SetFlag(BaseEntity.Flags.Reserved8, true);
item?.SetFlag(BaseEntity.Flags.On, true);
item?.SendNetworkUpdate();
}
NeonSign neonSign = item as NeonSign;
if (neonSign != null) {
neonSign.SetFlag(BaseEntity.Flags.On, true);
neonSign.SetFlag(BaseEntity.Flags.Reserved8, true);
neonSign.isAnimating = true;
neonSign.InvokeRepeating(neonSign.animationLoopAction, 2f, 2f);
neonSign.SendNetworkUpdate();
}
SnowMachine snowMachine = item as SnowMachine;
if (snowMachine != null) {
snowMachine.SetFlag(BaseEntity.Flags.Reserved8, true);
snowMachine.SetFlag(BaseEntity.Flags.Reserved7, false);
snowMachine.SetFlag(BaseEntity.Flags.Reserved6, true);
snowMachine.SetFlag(BaseEntity.Flags.Reserved5, true);
}
DecayEntity decayEntety = item as DecayEntity;
if (decayEntety != null) {
decayEntety.decay = null;
decayEntety.decayVariance = 0;
decayEntety.ResetUpkeepTime();
decayEntety.DecayTouch();
}
DeployableBoomBox boomBox = item as DeployableBoomBox;
if (boomBox != null) {
if (config.settings.useRadio) {
NextTick(() => {
boomBox.BoxController.CurrentRadioIp = config.settings.RadioStation;
boomBox.BoxController.ConditionLossRate = 0;
boomBox.BoxController.baseEntity.ClientRPC(null, "OnRadioIPChanged", boomBox.BoxController.CurrentRadioIp);
if (!boomBox.BoxController.IsOn()) {
boomBox.BoxController.ServerTogglePlay(true);
}
boomBox.BoxController.baseEntity.SendNetworkUpdate(global : : BasePlayer.NetworkQueue.Update);
}
);
}
}
BuildingBlock build = item as BuildingBlock;
if (build != null) {
build.StopBeingDemolishable();
build.StopBeingRotatable();
}
if (item is ComputerStation) {
chairNpc = item as ComputerStation;
continue;
}
Door door = item as Door;
if (door != null) {
door.pickup.enabled = false;
door.canTakeLock = false;
door.canTakeCloser = false;
}
item.SetFlag(BaseEntity.Flags.Busy, true);
item.SetFlag(BaseEntity.Flags.Locked, true);
}
if(chairNpc != null) InitializeNPC(chairNpc.transform.position);
PrintWarning(GetLang("XDQUEST_CopyPasteSuccessfully"));
}
catch (Exception ex) {
PrintError(GetLang("XDQUEST_BuildingPasteError"));
Log(ex.InnerException.Message, "LogError");
}
}
#endregion
#region Scripts
private class ZoneTrigger : FacepunchBehaviour {
private float ZoneRadius;
private Vector3 Position;
private SphereCollider sphereCollider;
private void Awake() {
gameObject.layer = (int)Layer.Reserved1;
gameObject.name = "ZoneTrigger1866";
enabled = false;
}
public void Activate(Vector3 pos, float radius) {
Position = pos;
ZoneRadius = radius;
transform.position = Position;
transform.rotation = new Quaternion();
UpdateCollider();
gameObject.SetActive(true);
enabled = true;
}
private void OnTriggerEnter(Collider col) {
BasePlayer player = col.GetComponentInParent<BasePlayer>();
if (player != null && !player.IsNpc && player.userID.IsSteamId()) {
if (player.IsVisible(Instance.npc.eyes.position)) {
int mIndex = Random.Range(0, Instance.config.settingsSoundNPC.heySound.Count);
Instance.SoundPlay(Instance.config.settingsSoundNPC.heySound[mIndex]);
}
}
}
private void OnTriggerExit(Collider col) {
BasePlayer player = col.GetComponentInParent<BasePlayer>();
if (player != null && !player.IsNpc && player.userID.IsSteamId()) {
if (player.IsVisible(Instance.npc.eyes.position)) {
int mIndex = Random.Range(0, Instance.config.settingsSoundNPC.byeSound.Count);
Instance.SoundPlay(Instance.config.settingsSoundNPC.byeSound[mIndex]);
}
player.SendConsoleCommand("CloseMainUI");
if (Instance.openQuestPlayers.Contains(player.userID)) {
CuiHelper.DestroyUi(player, MiniQuestList);
Instance.OpenMQL_CMD(player);
}
}
}
private void OnDestroy() {
Destroy(gameObject);
CancelInvoke();
}
private void UpdateCollider() {
sphereCollider = gameObject.GetComponent<SphereCollider>();
{
if (sphereCollider == null) {
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.isTrigger = true;
sphereCollider.name = "ZoneTrigger1866";
}
sphereCollider.radius = ZoneRadius;
}
}
}
private class SafeZone : MonoBehaviour {
private Vector3 Position;
private float Radius;
private void Awake() {
gameObject.layer = (int)Layer.Reserved1;
gameObject.name = "NpcZonesOrRadiation";
var rigidbody = gameObject.AddComponent<Rigidbody>();
rigidbody.useGravity = false;
rigidbody.isKinematic = true;
}
public void Activate(Vector3 pos, float radius) {
Position = pos;
Radius = radius;
transform.position = Position;
UpdateCollider();
gameObject.SetActive(true);
enabled = true;
var safeZone = gameObject.GetComponent<TriggerSafeZone>();
safeZone = safeZone ?? gameObject.AddComponent<TriggerSafeZone>();
safeZone.interestLayers = LayerMask.GetMask("Player (Server)");
safeZone.enabled = true;
}
private void OnDestroy() {
Destroy(gameObject);
}
private void UpdateCollider() {
var sphereCollider = gameObject.GetComponent<SphereCollider>();
{
if (sphereCollider == null) {
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.isTrigger = true;
}
sphereCollider.radius = Radius;
}
}
}
#endregion
#region Hooks
#region QuestHook
#region Type Upgrade
object OnStructureUpgrade(BaseCombatEntity entity, BasePlayer player, BuildingGrade.Enum grade) {
QuestProgress(player, QuestType.Grade, ((int)grade).ToString());
return null;
}
#endregion
#region IQPlagueSkill
void StudySkill(BasePlayer player, string name) => QuestProgress(player, QuestType.IQPlagueSkill, name);
#endregion
#region HeadReward
void KillHead(BasePlayer player) => QuestProgress(player, QuestType.IQHeadReward);
#endregion
#region IqCase
void OpenCase(BasePlayer player, string name) => QuestProgress(player, QuestType.IQCases, name);
#endregion
#region OreBonus
void RadOreGive(BasePlayer player, Item item) => QuestProgress(player, QuestType.OreBonus, item.info.shortname, "", null, item.amount);
#endregion
#region Chinook
void LootHack(BasePlayer player) => QuestProgress(player, QuestType.XDChinookIvent);
#endregion
#region Gather
private void OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
NextTick(() => {
BasePlayer player = entity as BasePlayer;
;
if (player != null) QuestProgress(player, QuestType.Gather, item.info.shortname, "", null, item.amount);
}
);
}
void OnDispenserBonus(ResourceDispenser dispenser, BaseEntity entity, Item item) => OnDispenserGather(dispenser, entity, item);
void OnCollectiblePickup(Item item, BasePlayer player) => QuestProgress(player, QuestType.Gather, item.info.shortname, "", null, item.amount);
#endregion
#region Craft
void OnItemCraftFinished(ItemCraftTask task, Item item) => QuestProgress(task.owner, QuestType.Craft, task.blueprint.targetItem.shortname, "", null, item.amount);
#endregion
#region Research
void OnTechTreeNodeUnlock(Workbench workbench, TechTreeData.NodeInstance node, BasePlayer player) => QuestProgress(player, QuestType.Research, node.itemDef.shortname);
void OnItemResearch(ResearchTable table, Item targetItem, BasePlayer player) => QuestProgress(player, QuestType.Research, targetItem.info.shortname);
#endregion
#region Deploy
private void OnEntityBuilt(Planner plan, GameObject go) {
BasePlayer player = plan?.GetOwnerPlayer();
if (player == null || go == null || plan.GetItem() == null) return;
BaseEntity ent = go.ToBaseEntity();
if (ent == null || ent.skinID == 11543256361) return;
QuestProgress(player, QuestType.Deploy, plan.GetItem().info.shortname);
}
#endregion
#region Loot
private static Dictionary<BasePlayer, List<UInt64>> LootersListCarte = new Dictionary<BasePlayer, List<UInt64>>();
private void OnLootEntity(BasePlayer player, LootContainer entity) {
if (entity == null || entity.net == null || player == null || entity.OwnerID.IsSteamId()) return;
if (!LootersListCarte.ContainsKey(player)) LootersListCarte.Add(player, new List<UInt64> {
}
);
UInt64 netId = entity.net.ID;
if (LootersListCarte[player].Contains(netId)) return;
QuestProgress(player, QuestType.Loot, "", "", entity.inventory.itemList);
LootersListCarte[player].Add(netId);
}
private void OnLootEntity(BasePlayer player, NPCPlayerCorpse entity) {
if (entity.OwnerID.IsSteamId() || entity == null) return;
if (!LootersListCarte.ContainsKey(player)) LootersListCarte.Add(player, new List<UInt64> {
}
);
UInt64 netId = entity.net.ID;
if (LootersListCarte[player].Contains(netId)) return;
QuestProgress(player, QuestType.Loot, "", "", entity.containers[0].itemList);
LootersListCarte[player].Add(netId);
}
private void OnContainerDropItems(ItemContainer container) {
if (container == null) return;
BaseEntity entity = container.entityOwner;
if (entity == null) return;
if (!entity.ShortPrefabName.Contains("barrel")) return;
foreach (Item lootitem in container.itemList) lootitem.SetFlag(global : : Item.Flag.Placeholder, true);
}
void OnItemPickup(Item item, BasePlayer player) {
if (item == null || !item.HasFlag(global : : Item.Flag.Placeholder)) return;
item.SetFlag(global : : Item.Flag.Placeholder, false);
QuestProgress(player, QuestType.Loot, item.info.shortname, item.skin.ToString(), count : item.amount);
}
#endregion
#region Swipe
private void OnCardSwipe(CardReader cardReader, Keycard card, BasePlayer player) {
global : : Item item = card.GetItem();
if (item != null && card.accessLevel == cardReader.accessLevel && item.conditionNormalized > 0f) {
QuestProgress(player, QuestType.Swipe, card.GetItem().info.shortname);
}
}
#endregion
#region EntityKill/взорвать/уничтожить что либо
void OnPlayerDeath(BasePlayer player, HitInfo info) {
if (player == null || info == null || player.userID < 2147483647) return;
BasePlayer attacker = info.InitiatorPlayer;
if (attacker == null) return;
if (IsFriends(player.userID, attacker.userID) || IsClans(player.UserIDString, attacker.UserIDString) || IsDuel(attacker.userID) || player.userID == attacker.userID) return;
QuestProgress(player, QuestType.EntityKill, "player");
}
void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
try {
if (entity == null || info == null) return;
string targetName = entity?.ShortPrefabName;
if (targetName.Contains("corpse") || targetName.Contains("servergibs") || targetName.Contains("player")) return;
if (targetName == "testridablehorse") {
targetName = "horse";
}
BasePlayer player = null;
if (info.InitiatorPlayer != null) player = info.InitiatorPlayer;
else if (entity.GetComponent<BaseHelicopter>() != null) {
PatrolHelicopterAI helicopterAI = entity?.GetComponent<PatrolHelicopterAI>();
if (helicopterAI == null) return;
player = helicopterAI._targetList[helicopterAI._targetList.Count - 1].ply;
}
if (player != null && !player.IsNpc && entity.ToPlayer() != player) {
QuestProgress(player, QuestType.EntityKill, targetName.ToLower());
}
}
catch (Exception) {
}
}
#endregion
#region (NEW) Покупки у НПС
void OnNpcGiveSoldItem(NPCVendingMachine machine, Item soldItem, BasePlayer buyer) => QuestProgress(buyer, QuestType.PurchaseFromNpc, soldItem.info.shortname, "", null, soldItem.amount);
#endregion
#region(NEW) Взлом ящика
void CanHackCrate(BasePlayer player, HackableLockedCrate crate) {
QuestProgress(player, QuestType.HackCrate, "", "", null);
}
#endregion
#region (NEW) RecycleItem (Игрок не должен выходить из интерфейса переработчика)
private Dictionary<uint, BasePlayer> recyclePlayer = new Dictionary<uint, BasePlayer>();
void OnRecyclerToggle(Recycler recycler, BasePlayer player) {
if (!recycler.IsOn()) {
if (recyclePlayer.ContainsKey(recycler.net.ID)) {
recyclePlayer.Remove(recycler.net.ID);
recyclePlayer.Add(recycler.net.ID, player);
}
else recyclePlayer.Add(recycler.net.ID, player);
}
else if (recyclePlayer.ContainsKey(recycler.net.ID)) recyclePlayer.Remove(recycler.net.ID);
}
void OnRecycleItem(Recycler recycler, Item item) {
if (recyclePlayer.ContainsKey(recycler.net.ID)) {
int num2 = 1;
if (item.amount > 1) num2 = Mathf.CeilToInt(Mathf.Min((float)item.amount, (float)item.info.stackable * 0.1f));
QuestProgress(recyclePlayer[recycler.net.ID], QuestType.RecycleItem, item.info.shortname, "", null, num2);
}
}
#endregion
#region (NEW) Growseedlings
void OnGrowableGathered(GrowableEntity plant, Item item, BasePlayer player) {
QuestProgress(player, QuestType
// ... [Code preview truncated due to length. Full source in attached file] ...