C#:
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using VLB;
using static NPCPlayerApex;
namespace Oxide.Plugins {
[Info("XDChinookIvent", "DezLife", "2.2.0")]
[Description("Авто ивент особый груз")]
public class XDChinookIvent : RustPlugin {
private static XDChinookIvent _;
[PluginReference]
Plugin IQChat, IQEconomic, XDNotifications, RustMap;
#region Var
static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
Vector3 vector;
private const int LAND_LAYERS = 1 << 4 | 1 << 8 | 1 << 16 | 1 << 21 | 1 << 23;
static double CurrentTime() => DateTime.UtcNow.Subtract(epoch).TotalSeconds;
public int InitStart;
public List<CH47Helicopter> ListHeli = new List<CH47Helicopter>();
private void AddNotify(BasePlayer player, string title, string description, string command = "", string cmdyes = "", string cmdno = "") {
var Setting = config.xDNotification;
Interface.Oxide.CallHook("AddNotify", player, title, description, Setting.Color, Setting.AlertDelete, Setting.SoundEffect, command, cmdyes, cmdno);
}
#endregion
#region Lang
private new void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["ChinookBuild"]
= "<color=#E66062>You cannot build in the event area</color>", ["ChinookStart"] = "<color=#608FE6>Chinook Special Cargo</color> already poured on the dumping site.\nReset square <color=#6ACB52>{0}</color>", ["ChinookIsDead"] = "<color=#608FE6>Chinook Special Cargo</color> crashed!!!", ["ChinookFinish"] = "<color=#608FE6>Chinook Special Cargo</color> dumping device (square <color=#6ACB52>{0}</color>),left to wait <color=#3DA9FF>{1}</color> \nMap mark G", ["ChinookIsDrop"] = "<color=#608FE6>Chinook Special Cargo</color> dropped a very valuable load (square <color=#6ACB52>{0}</color>)\n Map mark G", ["ChinookIsDropHack"] = "<color=#FFAB3D>Особый someone started to crack the load!</color>\nYou have 15 minutes to open it\nSquare (<color=#6ACB52>{0}</color>) Map mark G", ["ChinookIsDropHackEnd"] = "<color=#FFAB3D>Special cargo hacked but not tarnished!</color>", ["ChinookIsDropHackEndLoot"] = "Special cargo tied up by player <color=#6ACB52>{0}</color>.",
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
["ChinookBuild"]
= "<color=#E66062>Вы не можете строить в зоне ивента</color>", ["ChinookStart"] = "<color=#608FE6>Чинук с особым грузом</color> уже вылител на место сброса.\nКвадрат сброса <color=#6ACB52>{0}</color>\nБудте осторожны! Его охраняет патруль", ["ChinookIsDead"] = "<color=#608FE6>Чинук с особым грузом</color> потерпел крушения!!!", ["ChinookFinish"] = "<color=#608FE6>Чинук с особым грузом</color> прилител на место сброса (квадрат <color=#6ACB52>{0}</color>), осталось подождать <color=#3DA9FF>{1}</color> \n Метка на карте G", ["ChinookIsDrop"] = "<color=#608FE6>Чинук с особым грузом</color> сбросил очень ценный груз (квадрат <color=#6ACB52>{0}</color>)\n Метка на карте G", ["ChinookIsDropHack"] = "<color=#FFAB3D>Особый груз кто то начал взламывать!</color>\nУ тебя есть 15 минут до его открытие\nКвадрат (<color=#6ACB52>{0}</color>) Метка на карте G", ["ChinookIsDropHackEnd"] = "<color=#FFAB3D>Особый груз взломан но не залутан!</color>", ["ChinookIsDropHackEndLoot"] = "Особый груз залутан игроком <color=#6ACB52>{0}</color>.",
}
, this, "ru");
}
#endregion
#region Configuration
public static Configuration config = new Configuration();
public class Configuration {
public class ItemsDrop {
[JsonProperty("Short name")]
public string Shortname;
[JsonProperty("Skin ID ")]
public ulong SkinID;
[JsonProperty("Имя предмета")]
public string DisplayName;
[JsonProperty("Чертеж?")]
public bool BluePrint;
[JsonProperty("Мин кол")]
public int MinimalAmount;
[JsonProperty("макс кол")]
public int MaximumAmount;
[JsonProperty("Шанс выпадения предмета")]
public int DropChance;
}
public class ChinookSetings {
[JsonProperty("Настройка плавности/скорости спуска ящика")]
public float gravity;
[JsonProperty("Радиус запрета построек во время ивента")]
public int radius;
[JsonProperty("Время ожидания сброса")]
public int TimeStamp;
[JsonProperty("Время до самоуничтожения (Если никто не начнет взламывать)")]
public int CrateDestroy;
[JsonProperty("Минимальное колличевство игроков для запуска ивента")]
public int PlayersMin;
[JsonProperty("Раз в сколько часов будет летать чинук")]
public int TimeChinook;
[JsonProperty("Максимум предметов в 1 ящике")]
public int MaxItem;
[JsonProperty("Использовать стандартный лут в ящике (Если false, то будет выпадать ваш лут)")]
public bool customLoot;
[JsonProperty("Использовать кастомные позиции ?")]
public bool useCustomPos;
[JsonProperty("Кастомные позиции (/chinook addspawnpoint)")]
public List<Vector3> customPos = new List<Vector3>();
}
public class NpcChinook {
[JsonProperty("Спавнить нпс возле ящика ?")]
public bool npcUse;
[JsonProperty("Количество нпс")]
public int npcCount;
[JsonProperty("Хп нпс")]
public int healtNpc;
[JsonProperty("Рандомные ники нпс")]
public List<string> nickName = new List<string>();
}
public class MapMarker {
[JsonProperty("Создавать маркер на G карте ?")]
public bool markerUseG;
[JsonProperty("Названия маркера")]
public string nameMarkerG;
[JsonProperty("Цвет маркера")]
public string colorMarkerG;
[JsonProperty("Прзрачность маркера")]
public float alfaMarker;
[JsonProperty("Метки на карте RustMap")]
public bool rustMapUse;
[JsonProperty("Картинка для метки карты RustMap")]
public string imageForRustMap;
}
public class iqEconomic {
[JsonProperty("Использовать выдачу баланса IQEconomic?")]
public bool use;
[JsonProperty("минимальное количество монет")]
public int MinimalAmount;
[JsonProperty("максимальное количество монет")]
public int MaximumAmount;
[JsonProperty("Шанс выпадения монеты")]
public int chance;
}
public class XDNotification {
[JsonProperty("Включить поддержку XDNotifications(UI уведомления будут заменены на уведомление с XDNotifications)")]
public bool UseXDNotifications;
[JsonProperty("Цвет заднего фона уведомления(HEX)")]
public string Color;
[JsonProperty("Через сколько удалиться уведомление")]
public int AlertDelete;
[JsonProperty("Звуковой эффект")]
public string SoundEffect;
}
[JsonProperty("Настройки")]
public ChinookSetings chinook = new ChinookSetings();
[JsonProperty("Связка с IQEconomic")]
public iqEconomic iqEconomics = new iqEconomic();
[JsonProperty(PropertyName = "Настройка XDNotifications(Если есть)")]
public XDNotification xDNotification = new XDNotification();
[JsonProperty("Настройка отображения на картах")]
public MapMarker mapMarker = new MapMarker();
[JsonProperty("Настройка нпс")]
public NpcChinook npcChinook = new NpcChinook();
[JsonProperty("Выпадаемые предметы")]
public List<ItemsDrop> itemsDrops = new List<ItemsDrop>();
public static Configuration GetNewConfiguration() {
return new Configuration {
chinook = new ChinookSetings {
gravity = 0.7f, radius = 65, TimeStamp = 60, CrateDestroy = 600, PlayersMin = 20, TimeChinook = 7200, MaxItem = 7, customLoot = true, useCustomPos = false, customPos = new List<Vector3>()
}
, npcChinook = new NpcChinook {
npcUse = true, npcCount = 6, healtNpc = 150, nickName = new List<string> {
"Патрульный", "Защитник особого груза", "Опасность"
}
}
, mapMarker = new MapMarker {
markerUseG = true, alfaMarker = 0.4f, colorMarkerG = "#54bbb4", nameMarkerG = "Особый груз", rustMapUse = false, imageForRustMap = "https://i.imgur.com/x6qoCaK.png"
}
, iqEconomics = new iqEconomic {
use = false, chance = 90, MinimalAmount = 3, MaximumAmount = 7
}
, xDNotification = new XDNotification {
UseXDNotifications = false, Color = "#5A54DDFF", AlertDelete = 5, SoundEffect = ""
}
, itemsDrops = new List<ItemsDrop> {
new ItemsDrop {
Shortname = "halloween.surgeonsuit", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 1, DropChance = 70
}
, new ItemsDrop {
Shortname = "metal.facemask", SkinID = 1886184322, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 1, DropChance = 20
}
, new ItemsDrop {
Shortname = "door.double.hinged.metal", SkinID = 191100000, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 2, DropChance = 60
}
, new ItemsDrop {
Shortname = "rifle.bolt", SkinID = 0, DisplayName = "", BluePrint = true, MinimalAmount = 1, MaximumAmount = 1, DropChance = 10
}
, new ItemsDrop {
Shortname = "rifle.lr300", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 1, DropChance = 15
}
, new ItemsDrop {
Shortname = "pistol.revolver", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 3, DropChance = 60
}
, new ItemsDrop {
Shortname = "supply.signal", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 3, DropChance = 20
}
, new ItemsDrop {
Shortname = "explosive.satchel", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 3, DropChance = 5
}
, new ItemsDrop {
Shortname = "grenade.smoke", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 1, MaximumAmount = 20, DropChance = 45
}
, new ItemsDrop {
Shortname = "ammo.rifle", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 50, MaximumAmount = 120, DropChance = 35
}
, new ItemsDrop {
Shortname = "scrap", SkinID = 0, DisplayName = "", BluePrint = false, MinimalAmount = 100, MaximumAmount = 500, DropChance = 20
}
, new ItemsDrop {
Shortname = "giantcandycanedecor", SkinID = 0, DisplayName = "Новый год", BluePrint = false, MinimalAmount = 1, MaximumAmount = 5, DropChance = 70
}
,
}
}
;
}
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) LoadDefaultConfig();
}
catch {
PrintWarning("Ошибка чтения конфигурации 'oxide/config/', создаём новую конфигурацию!!");
LoadDefaultConfig();
}
NextTick(SaveConfig);
}
protected override void LoadDefaultConfig() => config = Configuration.GetNewConfiguration();
protected override void SaveConfig() => Config.WriteObject(config, true);
#endregion
#region Classes
#region Method By OxideBro
Vector3 RandomCircle(Vector3 center, float radius = 2) {
float ang = UnityEngine.Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
pos.y = GetGroundPosition(pos);
return pos;
}
static float GetGroundPosition(Vector3 pos) {
float y = TerrainMeta.HeightMap.GetHeight(pos);
RaycastHit hit;
if (Physics.Raycast(new Vector3(pos.x, pos.y + 200f, pos.z), Vector3.down, out hit, Mathf.Infinity, LayerMask.GetMask(new[] {
"Terrain", "World", "Default", "Construction", "Deployed"
}
)) && !hit.collider.name.Contains("rock_cliff")) return Mathf.Max(hit.point.y, y);
return y;
}
const string scientist = "assets/prefabs/npc/scientist/scientist.prefab";
private NPCPlayerApex InstantiateEntity(string type, Vector3 position) {
position.y = GetGroundPosition(position);
var gameObject = Instantiate.GameObject(GameManager.server.FindPrefab(type), position, new Quaternion());
gameObject.name = type;
SceneManager.MoveGameObjectToScene(gameObject, Rust.Server.EntityScene);
UnityEngine.Object.Destroy(gameObject.GetComponent<Spawnable>());
if (!gameObject.activeSelf) gameObject.SetActive(true);
NPCPlayerApex component = gameObject.GetComponent<NPCPlayerApex>();
return component;
}
void SpawnBots(HackableLockedCrate hack) {
for (int i = 0; i < config.npcChinook.npcCount; i++) {
NPCPlayerApex entity = null;
entity = InstantiateEntity(scientist, RandomCircle(hack.transform.position, 10));
entity.enableSaving = false;
entity.Spawn();
entity.IsInvinsible = false;
entity.startHealth = config.npcChinook.healtNpc;
entity.InitializeHealth(entity.startHealth, entity.startHealth);
entity.Stats.AggressionRange = entity.Stats.DeaggroRange = 150;
entity.CommunicationRadius = 0;
entity.displayName = config.npcChinook.nickName.GetRandom();
entity.GetComponent<Scientist>().LootPanelName = entity.displayName;
entity.CancelInvoke(entity.EquipTest);
Equip(entity);
entity.Stats.MaxRoamRange = 75f;
entity.NeverMove = true;
NPCMonitor npcMonitor = entity.gameObject.AddComponent<NPCMonitor>();
npcMonitor.Initialize(hack);
}
}
public HeldEntity GetFirstWeapon(BasePlayer player) {
foreach (Item item in player.inventory.containerBelt.itemList) {
if (item.CanBeHeld() && (item.info.category == ItemCategory.Weapon)) {
BaseProjectile projectile = item.GetHeldEntity() as BaseProjectile;
if (projectile != null) {
projectile.primaryMagazine.contents = projectile.primaryMagazine.capacity;
projectile.SendNetworkUpdateImmediate();
return item.GetHeldEntity() as HeldEntity;
}
}
}
return null;
}
private void Equip(BasePlayer player) {
HeldEntity weapon1 = GetFirstWeapon(player);
if (weapon1 != null) {
weapon1.SetHeld(true);
}
}
public class NPCMonitor : FacepunchBehaviour {
public NPCPlayerApex player {
get;
private set;
}
private List<Vector3> patrolPositions = new List<Vector3>();
private HackableLockedCrate hacks;
private Vector3 homePosition;
private int lastPatrolIndex = 0;
private void Awake() {
player = GetComponent<NPCPlayerApex>();
InvokeRepeating(UpdateDestination, 0f, 5.0f);
}
public void Initialize(HackableLockedCrate hack) {
this.hacks = hack;
homePosition = hacks.transform.position;
GeneratePatrolPositions();
}
private void UpdateDestination() {
if (player.AttackTarget == null) {
player.NeverMove = true;
float distance = (player.transform.position - homePosition).magnitude;
bool tooFar = distance > 20;
if (player.GetNavAgent == null || !player.GetNavAgent.isOnNavMesh) player.finalDestination = patrolPositions[lastPatrolIndex];
else {
if (Vector3.Distance(player.transform.position, patrolPositions[lastPatrolIndex]) < 5) lastPatrolIndex++;
if (lastPatrolIndex >= patrolPositions.Count) lastPatrolIndex = 0;
player.SetDestination(patrolPositions[lastPatrolIndex]);
}
player.SetDestination(patrolPositions[lastPatrolIndex]);
player.SetFact(NPCPlayerApex.Facts.Speed, tooFar ? (byte)NPCPlayerApex.SpeedEnum.Run : (byte)NPCPlayerApex.SpeedEnum.Walk, true, true);
}
else {
player.NeverMove = false;
player.IsStopped = false;
var attacker = player.AttackTarget as BasePlayer;
if (attacker == null) return;
if (attacker.IsDead()) Forget();
}
}
private void Forget() {
player.lastDealtDamageTime = Time.time - 21f;
player.SetFact(Facts.HasEnemy, 0, true, true);
player.SetFact(Facts.EnemyRange, 3, true, true);
player.SetFact(Facts.AfraidRange, 1, true, true);
player.AiContext.EnemyNpc = null;
player.AiContext.EnemyPlayer = null;
player.AttackTarget = null;
player.lastAttacker = null;
player.lastAttackedTime = Time.time - 31f;
player.LastAttackedDir = Vector3.zero;
player.SetDestination(patrolPositions[lastPatrolIndex]);
}
private void OnDestroy() {
if(player != null) player.KillMessage();
Destroy(gameObject);
}
private void GeneratePatrolPositions() {
for (int i = 0; i < 6; i++) {
Vector3 position = hacks.transform.position + (UnityEngine.Random.onUnitSphere * 20f);
position.y = TerrainMeta.HeightMap.GetHeight(position);
patrolPositions.Add(position);
}
enabled = true;
}
}
#endregion
private class ForceCrate : FacepunchBehaviour {
public HackableLockedCrate crate;
public bool isGrounded;
public Vector3 groundPos;
private BaseEntity chute;
private float deathTime;
private float speed = 1;
private void Awake() {
crate = GetComponent<HackableLockedCrate>();
groundPos = new Vector3(crate.transform.position.x, TerrainMeta.HeightMap.GetHeight(crate.transform.position), crate.transform.position.z);
deathTime = UnityEngine.Time.time + config.chinook.CrateDestroy;
speed = config.chinook.gravity;
isGrounded = false;
InitChute();
}
private void Update() {
if (!isGrounded) {
if (Physics.Raycast(new Ray(crate.transform.position, Vector3.down), 2f, LAND_LAYERS)) {
OnLanded();
return;
}
crate.transform.position = new Vector3(crate.transform.position.x, crate.transform.position.y - 0.015f * speed, crate.transform.position.z);
}
if (UnityEngine.Time.time >= deathTime) {
if (crate.HasFlag(BaseEntity.Flags.Reserved1)) {
deathTime += +320;
return;
}
_.DestroyAll<NPCMonitor>();
crate.Kill();
Destroy(this);
return;
}
}
private void OnDestroy() {
chute?.Kill();
crate?.Kill();
_.RemoveMarker();
}
private void OnLanded() {
isGrounded = true;
transform.position = groundPos;
crate.GetComponent<Rigidbody>().useGravity = true;
crate.GetComponent<Rigidbody>().drag = 1.5f;
crate.GetComponent<Rigidbody>().isKinematic = false;
chute?.Kill();
if(config.npcChinook.npcUse) _.SpawnBots(crate);
}
private void InitChute() {
chute = GameManager.server.CreateEntity("assets/prefabs/misc/parachute/parachute.prefab", crate.transform.position);
chute.enableSaving = false;
chute.Spawn();
chute.SetParent(crate, false);
chute.transform.localPosition = Vector3.up;
Effect.server.Run("assets/bundled/prefabs/fx/smoke_signal_full.prefab", chute, 0, Vector3.zero, Vector3.zero, null, true);
}
}
#endregion
#region Commands
[ChatCommand("chinook")]
void ChinookCommands(BasePlayer player, string command, string[] args) {
if (!player.IsAdmin) {
PrintToChat(player, "У вас недостаточно прав!");
return;
}
if (args.Length == 0) {
PrintToChat(player, "/chinook addspawnpoint - Добавляет кастомную позицию для спавна чинука\n" + "/chinook call - Вызвать чинук преждевременно");
return;
}
switch (args[0]) {
case "addspawnpoint" : {
config.chinook.customPos.Add(player.transform.position);
PrintToChat(player, "Точка успешно добавлена");
SaveConfig();
break;
}
case "call" : {
if (ListHeli.Count >= 1) {
if (player != null) PrintToChat(player, "Ивент уже активен!");
return;
}
else {
ChinookHelicopterCall();
PrintToChat(player, "Вы вызвали чинук");
}
break;
}
}
}
#endregion
#region MainMetods
private void ChinookHelicopterCall() {
#region RandomSpawnPosition
float x = TerrainMeta.Size.x;
float y = 70f;
Vector3 val = Vector3Ex.Range(-1f, 1f);
val.y = 0f;
val.Normalize();
val *= x * 1f;
val.y = y;
#endregion
CH47Helicopter ChinookEnt = GameManager.server.CreateEntity("assets/prefabs/npc/ch47/ch47scientists.entity.prefab", val, new Quaternion(0f, 0f, 0f, 0f), true) as CH47Helicopter;
//////////////////
if (ChinookEnt == null) return;
CH47HelicopterAIController ChinookAI = ChinookEnt.GetComponent<CH47HelicopterAIController>();
ChinookAI.OwnerID = 112234;
ChinookAI.Spawn();
ChinookAI.SetMaxHealth(900000f);
ChinookAI.health = 900000f;
ChinookAI.Heal(900000f);
ListHeli.Add(ChinookEnt);
vector = config.chinook.useCustomPos ? config.chinook.customPos.GetRandom() : (Vector3)GetSpawnPoint();
string Cvadrat = "";
Cvadrat = GetGridString(vector);
ChinookAI.SetMoveTarget(vector);
#region Метки на карте
if (config.mapMarker.markerUseG) CreateMarker(vector, 5, config.mapMarker.nameMarkerG, config.mapMarker.nameMarkerG);
if (config.mapMarker.rustMapUse) RustMap?.Call("ApiAddPointUrl", config.mapMarker.imageForRustMap, Name, vector, config.mapMarker.nameMarkerG);
#endregion
SendChatAll(String.Format(lang.GetMessage("ChinookStart", this), Cvadrat));
Subscribe(nameof(CanBuild));
ChinookAI.SetLandingTarget(new Vector3(vector.x, TerrainMeta.HeightMap.GetHeight(vector) + 90f, vector.z));
Timer timers = null;
Vector3 CheckPos = Vector3.zero;
bool PositionOutCrate = false;
int i = 0;
timers = timer.Every(10 , () => {
if (ChinookAI.IsDead()) {
SendChatAll(lang.GetMessage("ChinookIsDead", this));
PrintWarning("Cninook is dead");
RemoveMarkers();
Unsubscribe(nameof(CanBuild));
ListHeli.Remove(ChinookAI);
timers.Destroy();
return;
}
if (Vector3.Distance(ChinookAI.transform.position.xz(), vector.xz()) < 10f) {
PositionOutCrate = true;
SendChatAll(String.Format(lang.GetMessage("ChinookFinish", this), Cvadrat, FormatTime(TimeSpan.FromSeconds(config.chinook.TimeStamp))));
timers.Destroy();
timer.Once(config.chinook.TimeStamp, () => {
SendChatAll(String.Format(lang.GetMessage("ChinookIsDrop", this), Cvadrat));
Vector3 pos = ChinookAI.transform.position + Vector3.down * 5f;
CreateCrate(pos);
ChinookAI.ClearLandingTarget();
ChinookAI.DestroyShared();
}
);
}
#region Костыль века
if(i > 5 && !PositionOutCrate && Vector3.Distance(CheckPos, ChinookAI.transform.position) < 7f) {
SendChatAll(lang.GetMessage("ChinookIsDead", this));
Unsubscribe(nameof(CanBuild));
RemoveMarkers();
ChinookAI.Kill();
ListHeli.Remove(ChinookAI);
timers.Destroy();
return;
}
if (i > 5) CheckPos = ChinookAI.transform.position;
i++;
#endregion
}
);
}
private void CreateCrate(Vector3 position) {
Quaternion rot = Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f);
HackableLockedCrate CrateEnt = GameManager.server.CreateEntity("assets/prefabs/deployable/chinooklockedcrate/codelockedhackablecrate.prefab", position, rot, true) as HackableLockedCrate;
CrateEnt.enableSaving = false;
CrateEnt.GetComponent<Rigidbody>().useGravity = false;
CrateEnt.Spawn();
CrateEnt.OwnerID = 12436;
ForceCrate crate = CrateEnt.gameObject.AddComponent<ForceCrate>();
crate.crate = CrateEnt;
if (!config.chinook.customLoot) {
CrateEnt.inventory.itemList.Clear();
for (int i = 0; i < config.itemsDrops.Count; i++) {
var cfg = config.itemsDrops[i];
bool goodChance = UnityEngine.Random.Range(0, 100) >= (100 - cfg.DropChance);
if (goodChance && CrateEnt.inventory.itemList.Count <= config.chinook.MaxItem) {
if (cfg.BluePrint) {
var bp = ItemManager.Create(ResearchTable.GetBlueprintTemplate());
bp.blueprintTarget = ItemManager.FindItemDefinition(cfg.Shortname).itemid;
bp.MoveToContainer(CrateEnt.inventory);
}
else {
Item GiveItem = ItemManager.CreateByName(cfg.Shortname, Oxide.Core.Random.Range(cfg.MinimalAmount, cfg.MaximumAmount), cfg.SkinID);
if (!string.IsNullOrEmpty(cfg.DisplayName)) {
GiveItem.name = cfg.DisplayName;
}
GiveItem.MoveToContainer(CrateEnt.inventory);
}
}
}
if (config.iqEconomics.use && IQEconomic) {
bool TypeMoney = (bool)IQEconomic?.Call("API_MONEY_TYPE");
if (TypeMoney) {
bool goodChance = UnityEngine.Random.Range(0, 100) >= (100 - config.iqEconomics.chance);
if (goodChance) {
Item money = (Item)IQEconomic?.Call("API_GET_ITEM", UnityEngine.Random.Range(config.iqEconomics.MinimalAmount, config.iqEconomics.MaximumAmount));
money.MoveToContainer(CrateEnt.inventory);
}
}
}
}
CrateEnt.inventory.capacity = CrateEnt.inventory.itemList.Count;
CrateEnt.inventory.MarkDirty();
CrateEnt.SendNetworkUpdate();
}
#endregion
#region Hooks
private object CanBuild(Planner planner, Construction prefab, Construction.Target target) {
if (Vector3.Distance(planner.transform.position, vector) < config.chinook.radius) {
BasePlayer player = planner?.GetOwnerPlayer();
if (player != null) SendChatPlayer(lang.GetMessage("ChinookBuild", this), player);
return false;
}
return null;
}
void OnCrateHack(HackableLockedCrate crate) {
if(crate.OwnerID == 12436) {
SendChatAll(String.Format(lang.GetMessage("ChinookIsDropHack", this), GetGridString(crate.transform.position)));
}
}
void OnCrateHackEnd(HackableLockedCrate crate) {
if (crate.OwnerID == 12436) {
SendChatAll(lang.GetMessage("ChinookIsDropHackEnd", this));
}
}
void CanLootEntity(BasePlayer player, StorageContainer container) {
if (container is HackableLockedCrate && container.OwnerID == 12436) {
SendChatAll(String.Format(lang.GetMessage("ChinookIsDropHackEndLoot", this), player.displayName));
RemoveMarker();
timer.Once(30, () => Unsubscribe(nameof(CanBuild)));
container.OwnerID = 32456345;
Interface.Oxide.CallHook("LootHack", player);
}
}
void OnEntityDestroy(CH47HelicopterAIController info) {
CH47Helicopter ch47 = info.GetComponent<CH47Helicopter>();
if (ListHeli.Contains(ch47)) ListHeli.Remove(ch47);
}
void Unload() {
Unsubscribe(nameof(CanBuild));
RemoveMarker();
for (int i = 0; i < ListHeli.Count; i++) ListHeli[i].Kill();
ListHeli.Clear();
DestroyAll<ForceCrate>();
DestroyAll<NPCMonitor>();
}
void Init() {
Unsubscribe(nameof(CanBuild));
}
void OnServerInitialized() {
_ = this;
LoadDefaultMessages();
if(!config.chinook.useCustomPos) GenerateSpawnpoints();
InitStart = (int)CurrentTime();
timer.Every(config.chinook.TimeChinook, () => {
if (BasePlayer.activePlayerList.Count >= config.chinook.PlayersMin) {
ChinookHelicopterCall();
}
InitStart = (int)CurrentTime();
}
);
PrintWarning($"Ивент будет запущен через {FormatTime(TimeSpan.FromSeconds(config.chinook.TimeChinook))}");
}
#endregion
#region SpawnMetods
private HashSet<Vector3> AnomaleisPoint = new HashSet<Vector3>();
private int spawnCount;
private object GetSpawnPoint() {
Vector3 targetPos = AnomaleisPoint.ToList().GetRandom();
if (targetPos == Vector3.zero) return null;
List<BaseEntity> entities = Facepunch.Pool.GetList<BaseEntity>();
Vis.Entities(targetPos, 50f, entities, LayerMask.GetMask("Construction", "Deployable", "Trigger"));
int count = entities.Count;
Facepunch.Pool.FreeList(ref entities);
if (count > 0) {
AnomaleisPoint.Remove(targetPos);
--spawnCount;
if (spawnCount < 10) {
PrintWarning("Все точки закончены!\n" + "Начинаем генерировать новые...");
AnomaleisPoint.Clear();
GenerateSpawnpoints();
return null;
}
return GetSpawnPoint();
}
AnomaleisPoint.Remove(targetPos);
return targetPos;
}
private void GenerateSpawnpoints() {
PrintWarning("Генерация точек для чинука...");
for (int i = 0; i < 1500; i++) {
float max = TerrainMeta.Size.x / 2;
var success = FindNewPosition(new Vector3(0, 0, 0), max);
if (success is Vector3) {
Vector3 spawnPoint = (Vector3)success;
float height = TerrainMeta.HeightMap.GetHeight(spawnPoint);
if (spawnPoint.y >= height && !(spawnPoint.y - height > 1)) {
AnomaleisPoint.Add(spawnPoint);
}
}
}
spawnCount += AnomaleisPoint.Count;
PrintWarning($"{spawnCount} точек сгенерированно!");
}
private object FindNewPosition(Vector3 position, float max, bool failed = false) {
var targetPos = UnityEngine.Random.insideUnitCircle * max;
var sourcePos = new Vector3(position.x + targetPos.x, 300, position.z + targetPos.y);
var hitInfo = RayPosition(sourcePos);
var success = ProcessRay(hitInfo);
if (success == null) {
if (failed) return null;
else return FindNewPosition(position, max, true);
}
else if (success is Vector3) {
if (failed) return null;
else return FindNewPosition(new Vector3(sourcePos.x, ((Vector3)success).y, sourcePos.y), max, true);
}
else {
sourcePos.y = Mathf.Max((float)success, TerrainMeta.HeightMap.GetHeight(sourcePos));
return sourcePos;
}
}
private object ProcessRay(RaycastHit hitInfo) {
if (hitInfo.collider != null) {
if (hitInfo.collider?.gameObject.layer == LayerMask.NameToLayer("Water")) return null;
if (hitInfo.collider?.gameObject.layer == LayerMask.NameToLayer("Prevent Building")) return null;
if (hitInfo.GetEntity() != null) {
return hitInfo.point.y;
}
if (hitInfo.collider?.name == "areaTrigger") return null;
if (hitInfo.collider?.GetComponentInParent<SphereCollider>() || hitInfo.collider?.GetComponentInParent<BoxCollider>()) {
return hitInfo.collider.transform.position + new Vector3(0, -1, 0);
}
}
return hitInfo.point.y;
}
private RaycastHit RayPosition(Vector3 sourcePos) {
RaycastHit hitInfo;
Physics.Raycast(sourcePos, Vector3.down, out hitInfo);
return hitInfo;
}
#endregion
#region HelpMetods
private void RemoveMarker() {
RemoveMarkers();
RustMap?.Call("ApiRemovePointUrl", Name);
}
#region Метка на g карте
private void CreateMarker(Vector3 position, float refreshRate, string name, string displayName, float radius = 0.65f, string colorOutline = "00FFFFFF") {
var marker = new GameObject().AddComponent<CustomMapMarker>();
marker.name = name;
marker.displayName = displayName;
marker.radius = radius;
marker.position = position;
marker.refreshRate = refreshRate;
ColorUtility.TryParseHtmlString($"{config.mapMarker.colorMarkerG}", out marker.color1);
ColorUtility.TryParseHtmlString($"{colorOutline}", out marker.color2);
}
private void RemoveMarkers() {
foreach (var marker in UnityEngine.Object.FindObjectsOfType<CustomMapMarker>()) {
UnityEngine.Object.Destroy(marker);
}
}
private const string genericPrefab = "assets/prefabs/tools/map/genericradiusmarker.prefab";
private const string vendingPrefab = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
#region Scripts
private class CustomMapMarker : MonoBehaviour {
private VendingMachineMapMarker vending;
private MapMarkerGenericRadius generic;
public BaseEntity parent;
private bool asChild;
public float radius;
public Color color1;
public Color color2;
public string displayName;
public float refreshRate;
public Vector3 position;
public bool placedByPlayer;
private void Start() {
transform.position = position;
asChild = parent != null;
CreateMarkers();
}
private void CreateMarkers() {
vending = GameManager.server.CreateEntity(vendingPrefab, position).GetComponent<VendingMachineMapMarker>();
vending.markerShopName = displayName;
vending.enableSaving = false;
vending.Spawn();
generic = GameManager.server.CreateEntity(genericPrefab).GetComponent<MapMarkerGenericRadius>();
generic.color1 = color1;
generic.color2 = color2;
generic.radius = radius;
generic.alpha = config.mapMarker.alfaMarker;
generic.enableSaving = false;
generic.SetParent(vending);
generic.Spawn();
UpdateMarkers();
if (refreshRate > 0f) {
if (asChild) {
InvokeRepeating(nameof(UpdatePosition), refreshRate, refreshRate);
}
else {
InvokeRepeating(nameof(UpdateMarkers), refreshRate, refreshRate);
}
}
}
private void UpdatePosition() {
if (asChild == true) {
if (parent.IsValid() == false) {
Destroy(this);
return;
}
else {
var pos = parent.transform.position;
transform.position = pos;
vending.transform.position = pos;
}
}
UpdateMarkers();
}
private void UpdateMarkers() {
vending.SendNetworkUpdate();
generic.SendUpdate();
}
private void DestroyMakers() {
if (vending.IsValid()) {
vending.Kill();
}
if (generic.IsValid()) {
generic.Kill();
}
}
private void OnDestroy() {
DestroyMakers();
}
}
#endregion
#endregion
#region Узнаем квадрат
string GetGridString(Vector3 pos) {
char[] alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
pos.z = -pos.z;
pos += new Vector3(TerrainMeta.Size.x, 0, TerrainMeta.Size.z) *.5f;
var cubeSize = 146.14f;
int xCube = (int)(pos.x / cubeSize);
int zCube = (int)(pos.z / cubeSize);
int firstLetterIndex = (int)(xCube / alpha.Length) - 1;
string firstLetter = "";
if (firstLetterIndex >= 0) firstLetter = $"{alpha[firstLetterIndex]}";
var xStr = $"{firstLetter}{alpha[xCube % 26]}";
var zStr = $"{zCube}";
return $"{xStr}{zStr}";
}
private string NumberToString(int number) {
bool a = number > 25;
Char c = (Char)(65 + (a ? number - 26 : number));
return a ? "A" + c : c.ToString();
}
#endregion
private void DestroyAll<T>() {
UnityEngine.Object.FindObjectsOfType(typeof(T)).ToList().ForEach(UnityEngine.Object.Destroy);
}
public static string FormatTime(TimeSpan time, int maxSubstr = 5, string language = "ru") {
string result = string.Empty;
switch (language) {
case "ru" : int i = 0;
if (time.Days != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Days, "дней", "дня", "день")}";
i++;
}
if (time.Hours != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Hours, "часов", "часа", "час")}";
i++;
}
if (time.Minutes != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Minutes, "минут", "минуты", "минута")}";
i++;
}
if (time.Seconds != 0 && i < maxSubstr) {
if (!string.IsNullOrEmpty(result)) result += " ";
result += $"{Format(time.Seconds, "секунд", "секунды", "секунд")}";
i++;
}
break;
case "en" : result = string.Format("{0}{1}{2}{3}", time.Duration().Days > 0 ? $"{time.Days:0} day{(time.Days == 1 ? String.Empty : "s")}, " : string.Empty, time.Duration().Hours > 0 ? $"{time.Hours:0} hour{(time.Hours == 1 ? String.Empty : "s")}, " : string.Empty, time.Duration().Minutes > 0 ? $"{time.Minutes:0} minute{(time.Minutes == 1 ? String.Empty : "s")}, " : string.Empty, time.Duration().Seconds > 0 ? $"{time.Seconds:0} second{(time.Seconds == 1 ? String.Empty : "s")}" : string.Empty);
if (result.EndsWith(", ")) result = result.Substring(0, result.Length - 2);
if (string.IsNullOrEmpty(result)) result = "0 seconds";
break;
}
return result;
}
int API_GetTimeStartIvent() {
int RealTimeSecond = (int)CurrentTime();
int LastTime = config.chinook.TimeChinook - (RealTimeSecond - InitStart);
return LastTime;
}
public void SendChatAll(string Message, ConVar.Chat.ChatChannel channel = ConVar.Chat.ChatChannel.Global) {
if (config.xDNotification.UseXDNotifications && XDNotifications) {
foreach (BasePlayer player in BasePlayer.activePlayerList) AddNotify(player, "ОСОБЫЙ ГРУЗ", Message.Replace("\n", " "));
return;
}
if (IQChat) IQChat?.Call("API_ALERT", Message);
else BasePlayer.activePlayerList.ToList().ForEach(p => p.SendConsoleCommand("chat.add", channel, 0, Message));
}
public void SendChatPlayer(string Message, BasePlayer player, ConVar.Chat.ChatChannel channel = ConVar.Chat.ChatChannel.Global) {
if (IQChat) IQChat?.Call("API_ALERT_PLAYER", player, Message);
else player.SendConsoleCommand("chat.add", channel, 0, Message);
}
private static string Format(int units, string form1, string form2, string form3) {
var tmp = units % 10;
if (units >= 5 && units <= 20 || tmp >= 5 && tmp <= 9) return $"{units} {form1}";
if (tmp >= 2 && tmp <= 4) return $"{units} {form2}";
return $"{units} {form3}";
}
#endregion
}
}