C#:
using Facepunch;
using Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using ProtoBuf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using UnityEngine;
namespace Oxide.Plugins {
[Info("StaticLootables", "Raul-Sorin Sorban", "2.8.2")]
[Description("Loot containers that aren't normally supposed to be looted.")]
public class StaticLootables : RustPlugin {
public static StaticLootables Instance {
get;
private set;
}
public System.Random Randomizer {
get;
set;
}
= new System.Random();
public System.Random GetSeededRandomizer(int seed) {
return new System.Random(seed);
}
public Dictionary<string, string> PanelTypes {
get;
set;
}
= new Dictionary<string, string> {
["generic"]
= "Generic", ["genericsmall"] = "Generic - Small", ["generic_large"] = "Generic - Large", ["generic_resizable"] = "Generic - Resizable", ["locker"] = "Locker", ["lantern"] = "Lantern", ["mailboxcontents"] = "Mailbox Contents", ["photoframe"] = "Photo Frame", ["reclaim"] = "Reclaim", ["skulltrophy"] = "Skull Trophy", ["toolcupboard"] = "Tool Cupboard"
}
;
public List<string> PanelTypesList {
get;
set;
}
= new List<string>();
#region Permissions
public const string AdminPerm = "staticlootables.admin";
public const string EditorPerm = "staticlootables.editor";
public void RegisterPermissions() {
permission.RegisterPermission(AdminPerm, this);
permission.RegisterPermission(EditorPerm, this);
}
#endregion
#region Plugins
[PluginReference]
Plugin ImageLibrary;
[PluginReference]
Plugin QuickSort;
[PluginReference]
Plugin ZoneManager;
[PluginReference]
Plugin LootablesExt;
public void RefreshPlugins() {
if (ImageLibrary == null || !ImageLibrary.IsLoaded) {
ImageLibrary = plugins.Find("ImageLibrary");
ImageLibrary?.Load();
}
if (QuickSort == null || !QuickSort.IsLoaded) {
QuickSort = plugins.Find("QuickSort");
QuickSort?.Load();
}
if (ZoneManager == null || !ZoneManager.IsLoaded) {
ZoneManager = plugins.Find("ZoneManager");
ZoneManager?.Load();
}
if (LootablesExt == null || !LootablesExt.IsLoaded) {
LootablesExt = plugins.Find("LootablesExt");
LootablesExt?.Load();
}
}
#endregion
#region Raycast
private bool TryGetClosestRayPoint(BasePlayer player, out object closestEnt, out Vector3 closestHitpoint) {
var hits = Physics.RaycastAll(player.eyes.HeadRay(), Config.Distance);
var closestdist = 999999f;
closestHitpoint = player.eyes.position;
closestEnt = false;
foreach (var hit in hits) {
var colliderName = hit.collider.name.ToLower().Trim();
if (hit.distance < closestdist && !colliderName.Contains("preventbuilding") && !colliderName.Contains("prevent_building") && !colliderName.Contains("prevent_movement") && !colliderName.Contains("terrain") && !colliderName.Contains("player.prefab")) {
closestdist = hit.distance;
closestEnt = hit.collider;
closestHitpoint = hit.point;
}
}
hits = null;
if (closestEnt is bool) return false;
return true;
}
#endregion
#region CUI
public const string CUIInteractionName = "stlootable";
public const string CUIEditorInteractionName = "stlootableeditorinteraction";
public const string CUIEditorName = "stlootableeditor";
public const string CUICodeLockInputName = "stlootableclin";
public const string CUIPinAppendCommand = "staticlootables.pinappend";
public const string CUIPinApplyCommand = "staticlootables.pinapply";
public const string CUIPinUndoCommand = "staticlootables.pinundo";
public const string DamageColor = "1 0.3 0.3 0.8";
public const string HackingColor = "0.5 0.8 0.3 0.8";
public Dictionary<BasePlayer, PlayerInteraction?> CUIPlayers {
get;
set;
}
= new Dictionary<BasePlayer, PlayerInteraction?>(800);
public Dictionary<BasePlayer, TimeSince> TimedPlayers {
get;
set;
}
= new Dictionary<BasePlayer, TimeSince>(800);
public Dictionary<BasePlayer, Vector3> PositionCache {
get;
set;
}
= new Dictionary<BasePlayer, Vector3>(800);
public Dictionary<BasePlayer, string> PinCache {
get;
set;
}
= new Dictionary<BasePlayer, string>(800);
public Dictionary<BasePlayer, RootData.Lootable> LootingPlayers {
get;
set;
}
= new Dictionary<BasePlayer, RootData.Lootable>(800);
public List<BasePlayer> DrawnUI {
get;
set;
}
= new List<BasePlayer>();
public const int ProcessorCount = 2;
public struct PlayerInteraction {
public RootConfig.Interaction Interaction {
get;
set;
}
public RootLock Lock {
get;
set;
}
public RootHack Hack {
get;
set;
}
public RootData.Lootable Lootable {
get;
set;
}
public bool IsProcessingCommand {
get;
set;
}
public bool IsLoadingTimer {
get;
set;
}
public DateTime Timer {
get;
set;
}
public float TimerTime => (float)(DateTime.Now - Timer).TotalSeconds;
}
public void DrawInteraction(BasePlayer player, RootConfig.Interaction interaction, RootConfig.LootableDefinition definition, RootData.Lootable lootable, bool transition = true, bool enableDot = true, bool reset = true) {
UndrawInteraction(player, reset);
var container = new CuiElementContainer();
var background = container.Add(new CuiPanel {
Image = {
Color = $"0 0 0 0"
}
, RectTransform = {
AnchorMin = "0.36 0.36", AnchorMax = "0.64 0.738"
}
, CursorEnabled = false
}
, "Hud", CUIInteractionName);
var offset = "0 -10";
var fade = transition ? 0.3f : 0f;
var @lock = lootable != null && lootable.Lock != null ? lootable.Lock : null;
var hack = lootable != null && lootable.Hack != null ? lootable.Hack : null;
var i = default(PlayerInteraction?);
if (!CUIPlayers.ContainsKey(player)) CUIPlayers.Add(player, i = new PlayerInteraction {
Interaction = interaction, Lock = @lock, Lootable = lootable
}
);
else i = CUIPlayers[player];
if (i == null) return;
if (enableDot) container.Add(new CuiElement {
Parent = background, FadeOut = fade, Components = {
Death.GetRawImage(Config.Urls.DotUrl, fade : fade), new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = offset, OffsetMax = offset
}
}
}
);
if (Config.ShadowOpacity != 0f) container.Add(new CuiElement {
Parent = background, FadeOut = fade, Components = {
Death.GetRawImage(Config.Urls.ShadowUrl, color : $"1 1 1 {Config.ShadowOpacity}", fade : fade), new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = offset, OffsetMax = offset
}
}
}
);
container.Add(new CuiElement {
Parent = background, FadeOut = fade, Components = {
Death.GetRawImage(i.Value.IsProcessingCommand ? Config.Urls.LoadingUrl : lootable != null && lootable.IsBusy(player) ? Config.Urls.BusyUrl : (@lock == null ? (hack == null ? interaction.Icon : hack.IsHacking ? Config.Urls.HackingUrl : Config.Urls.HackUrl) : Config.Urls.LockUrl), fade : fade), new CuiRectTransformComponent {
AnchorMin = "0.46 0.53", AnchorMax = "0.54 0.63", OffsetMin = offset, OffsetMax = offset
}
}
}
);
container.Add(new CuiLabel {
Text = {
Text = $"<b>{(i.Value.IsProcessingCommand ? Config.CommandName : lootable != null && lootable.IsBusy(player) ? $" {
Config.BusyName
}
" : (@lock == null ? hack == null ? interaction.Text : hack.IsHacking ? Config.HackingName : Config.HackName : Config.LockName))}</b>".ToUpper(), FadeIn = fade, FontSize = 13, Font = "robotocondensed-regular.ttf", Align = TextAnchor.LowerCenter
}
, RectTransform = {
AnchorMin = $"0 0.435", AnchorMax = $"1 1"
}
}
, background);
if (lootable != null) {
if (lootable.IsLocked() && definition != null && definition.Lock != null) {
var value = lootable.Lock.Health;
var total = definition.Lock.Health * Config.LootableHealthMultiplier;
var count = lootable.Contents.Count;
DrawInteractionBar(background, container, value, total, fade, count, definition, lootable);
}
else if (lootable.Hack != null && definition != null && definition.Hack != null) {
var time = lootable.GetHackTime();
var totalTime = lootable.GetTotalHackingTime();
if (lootable.IsHacking()) {
DrawInteractionBar(background, container, totalTime - time, totalTime, fade, -1, definition, textOne : $"{totalTime - time:0}s", textTwo : $"Please wait...", hackMode : true);
}
}
}
if (i != null && i.Value.IsLoadingTimer) {
var span = DateTime.Now - i.Value.Timer;
var value = definition.Timer - (float)span.TotalSeconds;
var total = definition.Timer;
DrawInteractionBar(background, container, value, total, fade, 0, definition, textOne : $"{value:0}s", textTwo : "", hackMode : true);
}
CuiHelper.AddUi(player, container);
DrawnUI.Add(player);
}
public void UndrawInteraction(BasePlayer player, bool reset = true) {
if (reset && !CUIPlayers.ContainsKey(player)) return;
if (DrawnUI.Contains(player)) {
for (int i = 0; i < ProcessorCount; i++) {
CuiHelper.DestroyUi(player, CUICodeLockInputName);
}
for (int i = 0; i < ProcessorCount; i++) {
CuiHelper.DestroyUi(player, CUIInteractionName);
}
for (int i = 0; i < ProcessorCount; i++) {
CuiHelper.DestroyUi(player, CUIEditorInteractionName);
}
for (int i = 0; i < ProcessorCount; i++) {
CuiHelper.DestroyUi(player, CUIEditorName);
}
DrawnUI.Remove(player);
}
if (reset) CUIPlayers.Remove(player);
}
public void DrawInteractionBar(string background, CuiElementContainer container, float value, float total, float fade, int count = -1, RootConfig.LootableDefinition definition = null, RootData.Lootable lootable = null, string textOne = null, string textTwo = null, bool hackMode = false) {
container.Add(new CuiLabel {
Text = {
Text = textOne == null ? $"<b>{value:n0} / {total:n0}</b>".ToUpper() : textOne, FadeIn = fade, FontSize = 10, Font = "robotocondensed-regular.ttf", Align = TextAnchor.UpperRight
}
, RectTransform = {
AnchorMin = "0.3 0.9", AnchorMax = "0.7 1"
}
}
, background);
var bar = container.Add(new CuiPanel {
Image = {
Color = hackMode ? HackingColor : DamageColor, FadeIn = fade
}
, RectTransform = {
AnchorMin = "0.3 0.925", AnchorMax = "0.7 0.95"
}
, CursorEnabled = false
}
, background);
container.Add(new CuiPanel {
Image = {
Color = $"1 1 1 1", FadeIn = fade
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = $"{Scale(value, 0f, total, 0f, 1f)} 1"
}
, CursorEnabled = false
}
, bar);
if ((definition != null && Instance.Config.ShowLockedLootableApproximateCount) || textTwo != null) {
var randomizer = GetSeededRandomizer(lootable == null ? definition.InteractionIndex : (int)(lootable.Position.x + lootable.Position.y + lootable.Position.z + definition.InteractionIndex));
container.Add(new CuiLabel {
Text = {
Text = textTwo == null ? $"~{randomizer.Next(count - 2, count + 2):n0} items appx.".ToUpper() : textTwo, FadeIn = fade, FontSize = 10, Font = "robotocondensed-regular.ttf", Align = TextAnchor.UpperLeft
}
, RectTransform = {
AnchorMin = "0.3 0.9", AnchorMax = "0.7 1"
}
}
, background);
randomizer = null;
}
}
private void DrawCodeLockInput(BasePlayer player) {
if (!PinCache.ContainsKey(player)) PinCache.Add(player, string.Empty);
for (int i = 0; i < 5; i++) {
CuiHelper.DestroyUi(player, CUICodeLockInputName);
}
var container = new CuiElementContainer();
var background = container.Add(new CuiPanel {
Image = {
Color = $"0.3 0.3 0.3 0.9", Material = "assets/content/ui/uibackgroundblur.mat"
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1"
}
, CursorEnabled = true
}
, "Hud", CUICodeLockInputName);
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinApplyCommand}", Color = "0 0 0 0"
}
, Text = {
Text = string.Empty
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1"
}
}
, background);
var panel = container.Add(new CuiPanel {
Image = {
Color = $"0 0 0 0"
}
, RectTransform = {
AnchorMin = "0.425 0.35", AnchorMax = "0.575 0.7"
}
, CursorEnabled = false
}
, background);
container.Add(new CuiLabel {
Text = {
Text = $"<b>{Config.CodeInputTitle}</b>".ToUpper(), FontSize = 18, Font = "robotocondensed-regular.ttf", Align = TextAnchor.UpperCenter
}
, RectTransform = {
AnchorMin = $"0 0.75", AnchorMax = $"1 0.985"
}
}
, panel);
var pinText = "";
foreach (var number in PinCache[player]) pinText += $"{number} ";
var pin = container.Add(new CuiPanel {
Image = {
Color = $"0 0 0 0.5"
}
, RectTransform = {
AnchorMin = "0.25 0.78", AnchorMax = "0.75 0.875"
}
, CursorEnabled = false
}
, panel);
container.Add(new CuiLabel {
Text = {
Text = $"<b>{pinText.Trim()}</b>".ToUpper(), Color = "1 1 1 0.7", FontSize = 18, Font = "robotocondensed-regular.ttf", Align = TextAnchor.MiddleCenter
}
, RectTransform = {
AnchorMin = $"0 0", AnchorMax = $"1 1"
}
}
, pin);
#region Buttons
var offset = 0f;
var horizontalOffset = 12f;
var spacing = 37.5f;
var count = 9;
for (int i = 0; i < 3; i++) {
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinAppendCommand} {count - 2}", Color = "0.8 0.8 0.8 0.35"
}
, Text = {
Text = $"<b>{count - 2}</b>", Align = TextAnchor.MiddleCenter, Color = "0.15 0.15 0.15 0.7", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.2 0.62", AnchorMax = "0.35 0.75", OffsetMin = $"{horizontalOffset} {offset}", OffsetMax = $"{horizontalOffset} {offset}"
}
}
, panel);
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinAppendCommand} {count - 1}", Color = "0.8 0.8 0.8 0.35"
}
, Text = {
Text = $"<b>{count - 1}</b>", Align = TextAnchor.MiddleCenter, Color = "0.15 0.15 0.15 0.7", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.2 0.62", AnchorMax = "0.35 0.75", OffsetMin = $"{horizontalOffset + 32} {offset}", OffsetMax = $"{horizontalOffset + 32} {offset}"
}
}
, panel);
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinAppendCommand} {count}", Color = "0.8 0.8 0.8 0.35"
}
, Text = {
Text = $"<b>{count}</b>", Align = TextAnchor.MiddleCenter, Color = "0.15 0.15 0.15 0.7", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.2 0.62", AnchorMax = "0.35 0.75", OffsetMin = $"{horizontalOffset + 64} {offset}", OffsetMax = $"{horizontalOffset + 64} {offset}"
}
}
, panel);
offset -= spacing;
count -= 3;
}
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinAppendCommand} 0", Color = "0.8 0.8 0.8 0.35"
}
, Text = {
Text = $"<b>0</b>", Align = TextAnchor.MiddleCenter, Color = "0.15 0.15 0.15 0.7", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.2 0.62", AnchorMax = "0.515 0.75", OffsetMin = $"{horizontalOffset} {offset}", OffsetMax = $"{horizontalOffset} {offset}"
}
}
, panel);
var acceptButton = container.Add(new CuiButton {
Button = {
Command = $"{CUIPinApplyCommand}", Color = "0.2 0.85 0.2 0.35"
}
, Text = {
Text = string.Empty, Align = TextAnchor.MiddleCenter, Color = "0.15 0.15 0.15 0.7", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.535 0.62", AnchorMax = "0.68 0.75", OffsetMin = $"{horizontalOffset} {offset}", OffsetMax = $"{horizontalOffset} {offset}"
}
}
, panel);
container.Add(new CuiElement {
Parent = acceptButton, Components = {
Death.GetRawImage(Config.Urls.CheckUrl, color : "0.15 0.15 0.15 0.7"), new CuiRectTransformComponent {
AnchorMin = "0.2 0.22", AnchorMax = "0.8 0.78"
}
}
}
);
offset -= spacing;
container.Add(new CuiButton {
Button = {
Command = $"{CUIPinUndoCommand}", Color = "0.5 0.5 0.5 0.35"
}
, Text = {
Text = $"<b>C</b>", Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.4", Font = "robotocondensed-regular.ttf", FontSize = 19
}
, RectTransform = {
AnchorMin = "0.2 0.62", AnchorMax = "0.68 0.75", OffsetMin = $"{horizontalOffset} {offset}", OffsetMax = $"{horizontalOffset} {offset}"
}
}
, panel);
#endregion
CuiHelper.AddUi(player, container);
if (!CUIPlayers.ContainsKey(player)) CUIPlayers.Add(player, new PlayerInteraction());
}
public bool IsLootingLootable(BasePlayer player, RootData.Lootable lootable) {
if (!CUIPlayers.ContainsKey(player)) return false;
return CUIPlayers[player]?.Lootable == lootable;
}
[ConsoleCommand(CUIPinAppendCommand)]
private void PinAppendCommand(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (PinCache[player].Length >= 4) PinCache[player] = string.Empty;
PinCache[player] += arg.Args[0];
DrawCodeLockInput(player);
}
[ConsoleCommand(CUIPinApplyCommand)]
private void PinApplyCommand(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
var interaction = CUIPlayers[player];
if (string.IsNullOrEmpty(PinCache[player]) || PinCache[player].Length < 4) {
UndrawInteraction(player);
PinCache[player] = string.Empty;
return;
}
if (PinCache[player] == interaction?.Lootable.Hack.Code) {
var definition = interaction?.Lootable.GetDefinition();
var interactionDefinition = Config.Interactions[definition.InteractionIndex];
PlayerLootContainer(player, definition, interaction?.Lootable, interactionDefinition, definition.Panel);
}
else {
player.ChatMessage($"Wrong code for that static lootable container.");
}
PinCache[player] = string.Empty;
UndrawInteraction(player);
}
[ConsoleCommand(CUIPinUndoCommand)]
private void PinUndoCommand(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (!string.IsNullOrEmpty(PinCache[player])) PinCache[player] = PinCache[player].Substring(0, PinCache[player].Length - 1);
DrawCodeLockInput(player);
}
#endregion
#region Override
public class Death {
public static string GetImage(string url, bool isPng = true, ulong imageId = 0) {
try {
var name = GetStringChecksum(url);
if (string.IsNullOrEmpty(url)) return string.Empty;
if ((bool)Instance.ImageLibrary.Call("HasImage", name, imageId)) {
var success = Instance.ImageLibrary.Call("GetImage", name, imageId);
return !isPng ? null : (string)success;
}
else {
Instance.ImageLibrary.Call("AddImage", url, name, imageId);
return !isPng ? url : null;
}
}
catch {
Instance.RefreshPlugins();
}
return string.Empty;
}
public static CuiRawImageComponent GetRawImage(string url, ulong skin = 0, string color = "", float fade = 0.3f) {
var _url = GetImage(url, isPng : false, imageId : skin);
var _png = GetImage(url, imageId : skin);
return new CuiRawImageComponent {
Url = _url, Png = _png, Color = color, FadeIn = fade
}
;
}
}
public static string GetStringChecksum(string value) {
if (value.Length <= 3) return null;
using (var md5 = MD5.Create()) {
var hash = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value));
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
private void OnServerInitialized() {
Instance = this;
RegisterPermissions();
timer.Every(20, () => {
ServerMgr.Instance.StartCoroutine(DoRefillingCalculations());
}
);
LoadContainer();
Log($"Initialized {Config.Definitions.Count:n0} lootables and {Data.Container.ItemContainer.itemList.Count:n0} persistent container(s). Poggers.");
CreateBehaviour();
foreach (var panel in PanelTypes) {
PanelTypesList.Add(panel.Key);
}
}
private void Loaded() {
RefreshPlugins();
if (ConfigFile == null) ConfigFile = new Core.Configuration.DynamicConfigFile($"{Manager.ConfigPath}/{Name}.json");
if (DataFile == null) DataFile = Interface.Oxide.DataFileSystem.GetFile($"{Name}_data");
if (!ConfigFile.Exists()) {
ConfigFile.WriteObject(Config ?? (Config = new RootConfig()));
}
else {
Config = ConfigFile.ReadObject<RootConfig>();
}
if (DataFile.Exists()) Data = DataFile.ReadObject<RootData>();
else Data = new RootData();
if (Instance == null) Instance = this;
}
private void Unload() {
foreach (var player in CUIPlayers.ToArray()) {
UndrawInteraction(player.Key);
player.Key.inventory.loot.Clear();
player.Key.inventory.loot.SendImmediate();
}
foreach (var player in Editors) {
UndrawEditor(player.Key, false);
UndrawEditorInteraction(player.Key);
}
foreach (var lootable in Data.Lootables) {
lootable.ClearContainer();
}
Data.Lootables.Clear();
Data.Container.ItemContainer?.Kill();
ClearBehaviour();
Instance = null;
}
private void OnServerSave() {
SaveContainer();
ConfigFile.WriteObject(Config ?? (Config = new RootConfig()));
DataFile.WriteObject(Data ?? (Data = new RootData()));
}
public void DoPlayerInput(BasePlayer player, InputState input) {
if (Config == null || Instance == null) return;
try {
if (Config.AsyncMode) {
if (!CoroutineBuffer.ContainsKey(player.userID)) CoroutineBuffer.Add(player.userID, true);
else if (CoroutineBuffer[player.userID] && !input.WasJustPressed(BUTTON.USE) && !input.WasJustPressed(BUTTON.RELOAD)) return;
CoroutineBuffer[player.userID] = true;
player.StartCoroutine(DoPlayerInputCoroutine(player, input));
}
else {
var loot = (RootData.Lootable)null;
if (LootingPlayers.TryGetValue(player, out loot) && loot.TimeSinceDistanceCheck >= 0.5f) {
if (Vector3.Distance(player.eyes.position, PositionCache[player]) > Config.Distance) {
player.inventory.loot.Clear();
player.inventory.loot.SendImmediate();
}
loot.TimeSinceDistanceCheck = 0;
}
object closestEnt;
Vector3 closestHitpoint;
if (!TryGetClosestRayPoint(player, out closestEnt, out closestHitpoint)) {
if (CUIPlayers.ContainsKey(player)) {
var i = CUIPlayers[player].GetValueOrDefault();
i.Timer = DateTime.Now;
i.IsLoadingTimer = false;
i.Lootable?.CancelHack();
CUIPlayers[player] = i;
}
UndrawEditorInteraction(player);
UndrawInteraction(player);
return;
}
var obj = (closestEnt as Collider)?.gameObject;
if (closestEnt == null || obj == null) return;
var name = obj.transform.GetRecursiveName().ToLower();
if (IsInEditMode(player)) {
if (input.WasJustPressed(BUTTON.RELOAD)) {
input.SwallowButton(BUTTON.RELOAD);
var editor = GetOrCreateEditor(player);
if (!editor.IsEditing) DrawEditor(player, EditModes.Settings);
}
if (!EditorInteractionPlayers.ContainsKey(player) || (EditorInteractionPlayers.ContainsKey(player) && EditorInteractionPlayers[player] != obj.transform)) {
if (!EditorInteractionPlayers.ContainsKey(player)) EditorInteractionPlayers.Add(player, obj.transform);
else EditorInteractionPlayers[player] = obj.transform;
DrawEditorInteraction(player, obj.transform);
}
if (input.WasJustPressed(BUTTON.USE)) {
input.SwallowButton(BUTTON.USE);
var editor = GetOrCreateEditor(player);
var definition = GetDefinition(obj.transform);
Editor.StartEdit(definition ?? new RootConfig.LootableDefinition(obj.transform.name.Split(' ')[0].ToLower(), 2, new List<RootLootableItemDefinition>()), editor.EditingDefinition);
editor.Transform = obj.transform;
DrawEditor(player);
}
}
else {
if (Config.MiddleMouseInfo && input.WasJustPressed(BUTTON.FIRE_THIRD) && permission.UserHasPermission(player.UserIDString, AdminPerm) && !player.IsHoldingEntity<BaseEntity>()) {
input.SwallowButton(BUTTON.FIRE_THIRD);
var ids = ZoneManager?.Call<string[]>("GetPlayerZoneIDs", player) ?? new string[0];
player.ChatMessage($"<size=10>Looking at:</size>\n<color=orange>{name}</color>\n" + $"<size=10>Zone(s) [{ids.Length:n0}]:</size>\n<color=orange>{(ids.Length == 0 ? "-" : string.Join(", ", ids))}</color>");
return;
}
var farAway = Vector3.Distance(closestHitpoint, player.transform.position) > Config.Distance;
var definition = GetDefinition(obj);
var interaction = definition == null ? null : Config.Interactions[definition.InteractionIndex];
var lootable = GetLootable(player, obj, definition, false);
var playerInteraction = !CUIPlayers.ContainsKey(player) ? null : CUIPlayers[player];
if (definition != null && definition.HasTimer() && lootable == null) lootable = GetLootable(player, obj, definition, true);
if (lootable != null && !lootable.IsLocked() && definition.HasTimer()) {
if (playerInteraction == null && !player.inventory.loot.IsLooting()) {
DrawInteraction(player, interaction, definition, lootable, true, reset : false);
}
var val = playerInteraction.GetValueOrDefault();
if (input.IsDown(BUTTON.USE)) {
if (!PositionCache.ContainsKey(player)) PositionCache.Add(player, closestHitpoint);
else PositionCache[player] = closestHitpoint;
if (input.WasJustPressed(BUTTON.USE)) {
val.Timer = DateTime.Now;
val.IsLoadingTimer = true;
CUIPlayers[player] = val;
DrawInteraction(player, interaction, definition, lootable, true, reset : false);
}
else if (val.IsLoadingTimer && val.TimerTime >= definition.Timer) {
input.SwallowButton(BUTTON.USE);
val.Timer = DateTime.Now;
val.IsLoadingTimer = false;
PlayerLootContainer(player, definition, lootable, interaction, definition.Panel);
CUIPlayers[player] = val;
UndrawInteraction(player, true);
return;
}
}
else {
val.IsLoadingTimer = false;
CUIPlayers[player] = val;
if (input.WasDown(BUTTON.USE) && !player.inventory.loot.IsLooting()) {
DrawInteraction(player, interaction, definition, lootable, false, reset : false);
}
}
CUIPlayers[player] = val;
if (!TimedPlayers.ContainsKey(player) || (TimedPlayers.ContainsKey(player) && TimedPlayers[player] >= Config.DamageCUIRefreshRate / 2)) {
if (input.IsDown(BUTTON.USE) && !player.inventory.loot.IsLooting()) DrawInteraction(player, interaction, definition, lootable, false, reset : false);
if (TimedPlayers.ContainsKey(player)) TimedPlayers[player] = 0f;
else TimedPlayers.Add(player, 0f);
}
return;
}
else {
var val = playerInteraction.GetValueOrDefault();
val.Timer = DateTime.Now;
val.IsLoadingTimer = false;
CUIPlayers[player] = val;
}
if (definition != null && !definition.Rule.IsValid(name, player)) return;
if (interaction == null && playerInteraction?.Interaction == interaction || playerInteraction?.Lock != lootable?.Lock || playerInteraction?.Interaction != interaction) {
if (lootable != null && lootable.HackingPlayer == player) {
lootable.CancelHack();
}
UndrawInteraction(player);
}
if (!farAway && definition != null && !player.inventory.loot.IsLooting() && !Config.IsFilteredOut(name) && Config.ZoneCheck(player)) {
if (definition.Lock != null || definition.Hack != null) lootable = GetLootable(player, obj, definition, true);
if (!CUIPlayers.ContainsKey(player) && !player.inventory.loot.IsLooting()) {
DrawInteraction(player, interaction, definition, lootable, true);
}
}
else {
if (lootable != null && lootable.HackingPlayer == player) {
lootable.CancelHack();
}
if (CUIPlayers.ContainsKey(player)) UndrawInteraction(player);
}
if (definition != null && !farAway) {
if (input.WasJustPressed(BUTTON.USE)) {
if (lootable == null) lootable = GetLootable(player, obj, definition, true);
if (lootable == null) return;
if (!PositionCache.ContainsKey(player)) PositionCache.Add(player, closestHitpoint);
else PositionCache[player] = closestHitpoint;
if (lootable.IsHackable() && !lootable.IsLocked() && !lootable.IsBusy(player)) {
lootable.StartHack(player);
DrawInteraction(player, interaction, definition, lootable, true, false);
lootable.Hack.HackingTimer = timer.Every(Config.DamageCUIRefreshRate, () => {
if (!CUIPlayers.ContainsKey(player) || Vector3.Distance(PositionCache[player], player.transform.position) > Config.Distance) {
if (lootable != null && lootable.HackingPlayer == player) {
lootable.CancelHack();
}
UndrawInteraction(player);
return;
}
if (lootable.HackHacked()) {
var hackNote = ItemManager.CreateByName("note");
hackNote.text = $"Successfully hacked! The code is: {lootable.Hack.Code}\n\nPress [RELOAD] to open the code input panel.";
player.GiveItem(hackNote);
lootable.Hack.HackedTimes++;
if (lootable != null && lootable.HackingPlayer == player) {
lootable.CancelHack();
}
}
var players = CUIPlayers?.ToArray();
for (int i = 0; i < players.Length; i++) {
if (players[i].Value?.Lootable == lootable) {
DrawInteraction(players[i].Key, interaction, definition, lootable, false, false);
}
}
players = null;
}
);
return;
}
else if (lootable.IsHacking()) return;
if (!lootable.IsLocked() && CUIPlayers[player].Value.TimerTime >= definition.Timer) {
PlayerLootContainer(player, definition, lootable, interaction, definition.Panel);
}
}
else if (input.WasJustPressed(BUTTON.RELOAD) && !lootable.IsLocked() && lootable.IsHackable()) {
DrawCodeLockInput(player);
}
}
}
}
}
catch {
}
// catch ( Exception ex ) { Puts ( $"{ex}" ); }
}
private void OnPlayerAttack(BasePlayer player, HitInfo info) {
if (Config == null || Instance == null) return;
object closestEnt;
Vector3 closestHitpoint;
if (!TryGetClosestRayPoint(player, out closestEnt, out closestHitpoint)) return;
var obj = (closestEnt as Collider)?.gameObject;
if (closestEnt == null || obj == null) return;
var lootable = GetLootable(player, obj);
if (lootable == null) return;
var definition = lootable.GetDefinition();
if (!lootable.IsLocked()) return;
var multiplier = info.damageProperties.fallback.GetMultiplier(HitArea.Head);
var damageAmount = GetRandomNumber(Config.DamageMultiplierMinimum, Config.DamageMultiplierMaximum) * multiplier;
lootable.Lock.Health -= damageAmount;
if (lootable.Lock.Health <= 0) {
lootable.Lock = null;
}
if (!TimedPlayers.ContainsKey(player) || (TimedPlayers.ContainsKey(player) && TimedPlayers[player] >= Config.DamageCUIRefreshRate) || !lootable.IsLocked()) {
ServerMgr.Instance.Invoke(() => {
var players = CUIPlayers.ToArray();
foreach (var cuiPlayer in players) {
if (cuiPlayer.Value?.Lootable == lootable) {
DrawInteraction(cuiPlayer.Key, Config.Interactions[definition.InteractionIndex], definition, lootable, false);
}
}
players = null;
}
, 0.2f);
TimedPlayers[player] = 0;
}
player?.SendConsoleCommand("gametip.hidegametip");
}
private void OnPlayerLootEnd(PlayerLoot inventory) {
var player = inventory.baseEntity;
if (player == null) return;
UndrawInteraction(player, reset : true);
if (LootingPlayers.ContainsKey(player)) LootingPlayers.Remove(player);
if (CUIPlayers.ContainsKey(player)) CUIPlayers[player] = null;
}
private void OnPluginLoaded(Plugin name) {
RefreshPlugins();
}
private void OnPluginUnloaded(Plugin name) {
RefreshPlugins();
}
private void OnNewSave(string filename) {
DataFile.WriteObject(Data = new RootData());
}
private void OnEntityKill(TimedExplosive entity) {
var colliders = Facepunch.Pool.GetList<Collider>();
Vis.Colliders(entity.transform.position, entity.explosionRadius, colliders);
foreach (var collider in colliders) {
var lootable = GetLootable(null, collider.gameObject);
if (lootable != null && lootable.IsLocked()) {
lootable.Lock.Health -= entity.damageTypes.Sum(x => x.amount);
if (lootable.Lock.Health <= 0) lootable.Lock = null;
}
}
Facepunch.Pool.FreeList(ref colliders);
}
private static string[] CommandSplit = new string[] {
" "
}
;
private static string[] EmptyArgs = new string[0];
public static void PlayerLootContainer(BasePlayer player, RootConfig.LootableDefinition definition, RootData.Lootable lootable, RootConfig.Interaction interaction, string lootPanel) {
player.serverInput.SwallowButton(BUTTON.USE);
var cui = Instance.CUIPlayers[player].GetValueOrDefault();
if (cui.IsProcessingCommand) return;
if (!string.IsNullOrEmpty(interaction.OpenEffect)) SendEffectTo(interaction.OpenEffect, player);
if (!string.IsNullOrEmpty(definition.Command)) {
cui.IsProcessingCommand = true;
Instance.CUIPlayers[player] = cui;
Instance.DrawInteraction(player, interaction, definition, lootable, false, false, reset : false);
Instance.timer.In(0.5f, () => {
var credential = ConsoleSystem.Option.Unrestricted.FromConnection(player.net.connection);
var split = definition.Command.Split(CommandSplit, StringSplitOptions.RemoveEmptyEntries);
var command = split.Length > 1 ? split[0] : definition.Command;
ConsoleSystem.Run(credential, command, split.Length <= 1 ? EmptyArgs : split.Skip(1).ToArray());
cui.IsProcessingCommand = false;
Instance.CUIPlayers[player] = cui;
Instance.DrawInteraction(player, interaction, definition, lootable, false, reset : false);
}
);
return;
}
player.inventory.loot.Clear();
player.inventory.loot.PositionChecks = false;
player.inventory.loot.entitySource = RelationshipManager.ServerInstance;
player.inventory.loot.itemSource = null;
player.inventory.loot.AddContainer(lootable.GetContainer(definition.ContainerSize, definition.Liquid, definition.AllowStack));
player.inventory.loot.MarkDirty();
player.inventory.loot.SendImmediate();
player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", lootPanel);
Instance.QuickSort?.CallHook("OnLootPlayer", player);
Instance.UndrawInteraction(player, true);
if (!Instance.LootingPlayers.ContainsKey(player)) Instance.LootingPlayers.Add(player, lootable);
}
public static ItemContainer CreateContainer(int capacity = 1, bool liquid = false) {
var container = new ItemContainer {
isServer = true, allowedContents = liquid ? ItemContainer.ContentsType.Liquid : ItemContainer.ContentsType.Generic, capacity = capacity
}
;
container.GiveUID();
return container;
}
public Dictionary<ulong, bool> CoroutineBuffer {
get;
private set;
}
= new Dictionary<ulong, bool>();
// ... [Code preview truncated due to length. Full source in attached file] ...