using System; using System.Collections.Generic; using System.Linq; using System.Text; using Facepunch; using Newtonsoft.Json; using Oxide.Core.Plugins; using Rust; using UnityEngine; namespace Oxide.Plugins { [Info("Survey Info", "Diesel_42o/Arainrr", "1.0.6", ResourceId = 2463)] [Description("Displays loot from survey charges")] internal class SurveyInfo : RustPlugin { #region Fields [PluginReference] private Plugin RustTranslationAPI; private const string PERMISSION_USE = "surveyinfo.use"; private const string PERMISSION_CHECK = "surveyinfo.check"; private static SurveyInfo _instance; private float _quarryWorkPerMinute; private float _pumpjackWorkPerMinute; private readonly HashSet _checkedCraters = new HashSet(); #endregion Fields #region Oxide Hooks private void Init() { _instance = this; permission.RegisterPermission(PERMISSION_USE, this); permission.RegisterPermission(PERMISSION_CHECK, this); cmd.AddChatCommand(configData.command, this, nameof(CmdCraterInfo)); } private void OnServerInitialized() { var quarry = GameManager.server.FindPrefab("assets/prefabs/deployable/quarry/mining_quarry.prefab")?.GetComponent(); if (quarry != null) { _quarryWorkPerMinute = 60f / quarry.processRate * quarry.workToAdd; } var pumpjack = GameManager.server.FindPrefab("assets/prefabs/deployable/oil jack/mining.pumpjack.prefab")?.GetComponent(); if (pumpjack != null) { _pumpjackWorkPerMinute = 60f / pumpjack.processRate * pumpjack.workToAdd; } } private void Unload() { foreach (var player in BasePlayer.activePlayerList) { UnityEngine.Object.Destroy(player.GetComponent()); } _instance = null; } private void OnAnalysisComplete(SurveyCrater crater, BasePlayer player) { if (player == null || crater == null) { return; } var deposit = ResourceDepositManager.GetOrCreate(crater.transform.position); if (deposit?._resources == null) { return; } var stringBuilder = new StringBuilder(); stringBuilder.AppendLine(Lang("MineralAnalysis", player.UserIDString)); stringBuilder.AppendLine(); stringBuilder.AppendLine(); var language = lang.GetLanguage(player.UserIDString); foreach (var resource in deposit._resources) { var workPerMinute = crater.ShortPrefabName == "survey_crater_oil" ? _pumpjackWorkPerMinute : _quarryWorkPerMinute; var pM = workPerMinute / resource.workNeeded; var displayName = GetItemDisplayName(language, resource.type); stringBuilder.AppendLine($"{displayName} : {pM:0.00} pM"); } var noteItem = ItemManager.CreateByName("note"); noteItem.text = stringBuilder.ToString(); player.GiveItem(noteItem, BaseEntity.GiveItemReason.PickedUp); } private void OnEntityKill(SurveyCharge surveyCharge) { if (surveyCharge == null) { return; } var player = surveyCharge.creatorEntity as BasePlayer; if (player == null) { return; } if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) { return; } var checkPosition = surveyCharge.transform.position; timer.Once(0.5f, () => { var list = Pool.GetList(); Vis.Entities(checkPosition, 1f, list, Layers.Mask.Default); var surveyCrater = list.FirstOrDefault(); if (surveyCrater != null) { var surveyItems = Pool.GetList(); if (!_checkedCraters.Contains(surveyCrater.net?.ID.Value ?? 0)) { var deposit = ResourceDepositManager.GetOrCreate(surveyCrater.transform.position); if (deposit != null) { foreach (var resource in deposit._resources) { surveyItems.Add(new SurveyItem { itemDefinition = resource.type, amount = resource.amount, workNeeded = resource.workNeeded }); } _checkedCraters.Add(surveyCrater.net?.ID.Value ?? 0); } } if (surveyItems.Count > 0) { SendMineralAnalysis(player, surveyItems, surveyCrater.ShortPrefabName == "survey_crater_oil"); } Pool.FreeList(ref surveyItems); } Pool.FreeList(ref list); }); } private void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem) { if (player == null || newItem == null) { return; } if (oldItem?.info?.shortname != "surveycharge" && newItem.info.shortname == "surveycharge") { if (!permission.UserHasPermission(player.UserIDString, PERMISSION_CHECK)) { return; } UnityEngine.Object.Destroy(player.GetComponent()); player.gameObject.AddComponent(); } } #endregion Oxide Hooks #region Component private class SurveyerComponent : MonoBehaviour { private BasePlayer _player; private Item _heldItem; private float _lastCheck; private ulong _currentItemID; private void Awake() { _player = GetComponent(); _heldItem = _player.GetActiveItem(); _currentItemID = _player.svActiveItemID.Value; } private void Update() { if (_player == null || !_player.IsConnected || !_player.CanInteract()) { Destroy(this); return; } if (_player.svActiveItemID.Value != _currentItemID) { _heldItem = _player.GetActiveItem(); if (_heldItem != null && _heldItem.info.shortname != "surveycharge") { Destroy(this); return; } _currentItemID = _player.svActiveItemID.Value; } if (Time.realtimeSinceStartup - _lastCheck >= 0.5f) { if (!_player.serverInput.IsDown(BUTTON.FIRE_SECONDARY)) { return; } var surveyPosition = GetSurveyPosition(); _instance.Print(_player, CanSpawnCrater(surveyPosition) ? _instance.Lang("CanSpawnCrater", _player.UserIDString) : _instance.Lang("CantSpawnCrater", _player.UserIDString)); _lastCheck = Time.realtimeSinceStartup; } } private Vector3 GetSurveyPosition() { RaycastHit hitInfo; return Physics.Raycast(_player.eyes.HeadRay(), out hitInfo, 100f, Layers.Solid) ? hitInfo.point : _player.transform.position; } private static bool CanSpawnCrater(Vector3 position) { if (WaterLevel.Test(position, false, false)) { return false; } var deposit = ResourceDepositManager.GetOrCreate(position); if (deposit?._resources == null || Time.realtimeSinceStartup - deposit.lastSurveyTime < 10f) { return false; } RaycastHit hitOut; if (!TransformUtil.GetGroundInfo(position, out hitOut, 0.3f, 8388608)) { return false; } var list = Pool.GetList(); Vis.Entities(position, 10f, list, 1); var flag = list.Count > 0; Pool.FreeList(ref list); if (flag) { return false; } foreach (var resource in deposit._resources) { if (resource.spawnType == ResourceDepositManager.ResourceDeposit.surveySpawnType.ITEM && !resource.isLiquid && resource.amount >= 1000) { return true; } } return false; } } #endregion Component #region Chat Command private void CmdCraterInfo(BasePlayer player, string command, string[] args) { if (!permission.UserHasPermission(player.UserIDString, PERMISSION_USE)) { Print(player, Lang("NotAllowed", player.UserIDString)); return; } RaycastHit hitInfo; if (!Physics.Raycast(player.eyes.HeadRay(), out hitInfo, 10f, Layers.Mask.Default) || !(hitInfo.GetEntity() is SurveyCrater)) { Print(player, Lang("NotLookingAtCrater", player.UserIDString)); return; } var surveyCrater = hitInfo.GetEntity() as SurveyCrater; var surveyItems = Pool.GetList(); var deposit = ResourceDepositManager.GetOrCreate(surveyCrater.transform.position); if (deposit != null) { foreach (var resource in deposit._resources) { surveyItems.Add(new SurveyItem { itemDefinition = resource.type, amount = resource.amount, workNeeded = resource.workNeeded }); } } if (surveyItems.Count <= 0) { Print(player, Lang("NoMinerals", player.UserIDString)); return; } SendMineralAnalysis(player, surveyItems, surveyCrater.ShortPrefabName == "survey_crater_oil"); Pool.FreeList(ref surveyItems); } #endregion Chat Command #region Methods private void SendMineralAnalysis(BasePlayer player, List surveyItems, bool isOilCrater) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine(Lang("MineralAnalysis", player.UserIDString)); var language = lang.GetLanguage(player.UserIDString); foreach (var surveyItem in surveyItems) { var workPerMinute = isOilCrater ? _pumpjackWorkPerMinute : _quarryWorkPerMinute; var pM = workPerMinute / surveyItem.workNeeded; stringBuilder.AppendLine(Lang("MineralInfo", player.UserIDString, GetItemDisplayName(language, surveyItem.itemDefinition), surveyItem.amount, pM.ToString("0.00"))); } Print(player, stringBuilder.ToString()); } private struct SurveyItem { public ItemDefinition itemDefinition; public int amount; public float workNeeded; } #region RustTranslationAPI private string GetItemTranslationByShortName(string language, string itemShortName) { return (string)RustTranslationAPI.Call("GetItemTranslationByShortName", language, itemShortName); } private string GetItemDisplayName(string language, ItemDefinition itemDefinition) { string displayName; if (RustTranslationAPI != null) { displayName = GetItemTranslationByShortName(language, itemDefinition.shortname); if (!string.IsNullOrEmpty(displayName)) { return displayName; } } if (!configData.displayNames.TryGetValue(itemDefinition.displayName.english, out displayName)) { displayName = itemDefinition.displayName.english; configData.displayNames.Add(displayName, displayName); SaveConfig(); } return displayName; } #endregion RustTranslationAPI #endregion Methods #region ConfigurationFile private ConfigData configData; private class ConfigData { [JsonProperty(PropertyName = "Chat Command")] public string command = "craterinfo"; [JsonProperty(PropertyName = "Chat Prefix")] public string prefix = "[SurveyInfo]: "; [JsonProperty(PropertyName = "Chat SteamID Icon")] public ulong steamIDIcon = 0; [JsonProperty(PropertyName = "Display Names")] public Dictionary displayNames = new Dictionary(); } protected override void LoadConfig() { base.LoadConfig(); try { configData = Config.ReadObject(); if (configData == null) { LoadDefaultConfig(); } } catch (Exception ex) { PrintError($"The configuration file is corrupted. \n{ex}"); LoadDefaultConfig(); } SaveConfig(); } protected override void LoadDefaultConfig() { PrintWarning("Creating a new configuration file"); configData = new ConfigData(); } protected override void SaveConfig() { Config.WriteObject(configData); } #endregion ConfigurationFile #region LanguageFile private void Print(BasePlayer player, string message) { Player.Message(player, message, configData.prefix, configData.steamIDIcon); } private string Lang(string key, string id = null, params object[] args) { try { return string.Format(lang.GetMessage(key, this, id), args); } catch (Exception) { PrintError($"Error in the language formatting of '{key}'. (userid: {id}. lang: {lang.GetLanguage(id)}. args: {string.Join(" ,", args)})"); throw; } } protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "You don't have permission to use this command.", ["MineralAnalysis"] = "- Mineral Analysis -", ["MineralInfo"] = "{0} x{1} -- pM: {2} ", ["CanSpawnCrater"] = "A crater can be spawned at the position you are looking at.", ["CantSpawnCrater"] = "A crater cannot be spawned at the position you are looking at.", ["NotLookingAtCrater"] = "You must be looking at a crater.", ["NoMinerals"] = "There are no minerals in this crater" }, this); lang.RegisterMessages(new Dictionary { ["NotAllowed"] = "您没有权限使用该命令", ["MineralAnalysis"] = "- 矿物分析 -", ["MineralInfo"] = "{0} x{1} -- pM: {2} ", ["CanSpawnCrater"] = "您看着的位置 可以 勘探出矿物", ["CantSpawnCrater"] = "您看着的位置 不能 勘探出矿物", ["NotLookingAtCrater"] = "您必须看着一个矿坑", ["NoMinerals"] = "这个矿坑内没有任何矿物" }, this, "zh-CN"); } #endregion LanguageFile } }