Иконка ресурса

FREE RepairControl : 1.0.0 1.0.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Oxide.Plugins
{
  [Info("Repair Control", "Choanie", "1.0.0")]
  [Description("Stops vanilla's permanent max-condition penalty on repair, with a per-item blacklist and optional permission gating.")]
  public class RepairControl : RustPlugin
  {
    #region Fields

    private Configuration _config;
    private HashSet<string> _blacklist;

    private const float ConditionEpsilon = 0.01f;

    #endregion

    #region Config

    protected override void LoadDefaultConfig() => _config = Configuration.Default();

    protected override void LoadConfig()
    {
      base.LoadConfig();
      try
      {
        _config = Config.ReadObject<Configuration>();
        if (_config == null) throw new Exception();
      }
      catch
      {
        PrintWarning("Config file is corrupt or missing, creating a new one.");
        LoadDefaultConfig();
      }

      SaveConfig();
    }

    protected override void SaveConfig() => Config.WriteObject(_config);

    private class Configuration
    {
      public Settings Settings = new();

      public static Configuration Default() => new();
    }

    private class Settings
    {
      public bool Enabled = true;
      public bool UsePermission = false;
      public string Permission = "repaircontrol.use";
      public List<string> ItemBlacklist = new();
    }

    #endregion

    #region Lifecycle

    private void Init()
    {
      permission.RegisterPermission(_config.Settings.Permission, this);
      BuildBlacklist();
    }

    private void OnServerInitialized()
    {
      ValidateBlacklist();
      ApplyEngineFlags();

      if (_config.Settings.Enabled)
        FixExistingItems();
    }

    private void Unload()
    {
      foreach (var definition in ItemManager.GetItemDefinitions())
      {
        if (definition?.condition == null) continue;
        definition.condition.maintainMaxCondition = false;
      }
    }

    private void OnPlayerConnected(BasePlayer player)
    {
      if (!_config.Settings.Enabled || !PlayerPermitted(player)) return;

      FixPlayerInventory(player);
    }

    #endregion

    #region Commands

    [ChatCommand("repaircontrol")]
    private void CmdRepairControl(BasePlayer player, string command, string[] args)
    {
      if (!player.IsAdmin)
      {
        player.ChatMessage("You don't have permission to use this command.");
        return;
      }

      if (args.Length == 0)
      {
        player.ChatMessage(CommandUsage);
        return;
      }

      switch (args[0].ToLowerInvariant())
      {
        case "list":
          player.ChatMessage(BlacklistStatus());
          break;

        case "blacklist":
          player.ChatMessage(args.Length < 2 ? "Usage: /repaircontrol blacklist <shortname>" : AddToBlacklist(args[1]));
          break;

        case "unblacklist":
          player.ChatMessage(args.Length < 2 ? "Usage: /repaircontrol unblacklist <shortname>" : RemoveFromBlacklist(args[1]));
          break;

        default:
          player.ChatMessage(CommandUsage);
          break;
      }
    }

    private const string CommandUsage = "Usage: /repaircontrol <list|blacklist <shortname>|unblacklist <shortname>>";

    private string BlacklistStatus() =>
      $"Blacklisted (vanilla penalty applies): {(_blacklist.Count == 0 ? "(none)" : string.Join(", ", _blacklist))}";

    private string AddToBlacklist(string shortname)
    {
      shortname = shortname.Trim().ToLowerInvariant();
      if (string.IsNullOrEmpty(shortname)) return "Shortname cannot be empty.";
      if (ItemManager.FindItemDefinition(shortname) == null) return $"'{shortname}' does not match any known item shortname.";
      if (_config.Settings.ItemBlacklist.Any(s => string.Equals(s, shortname, StringComparison.OrdinalIgnoreCase)))
        return $"'{shortname}' is already blacklisted.";

      _config.Settings.ItemBlacklist.Add(shortname);
      ApplyBlacklistChange();
      return $"'{shortname}' is now blacklisted — it will lose max condition on repair like vanilla.";
    }

    private string RemoveFromBlacklist(string shortname)
    {
      shortname = shortname.Trim().ToLowerInvariant();
      if (_config.Settings.ItemBlacklist.RemoveAll(s => string.Equals(s, shortname, StringComparison.OrdinalIgnoreCase)) == 0)
        return $"'{shortname}' was not blacklisted.";

      ApplyBlacklistChange();
      return $"'{shortname}' removed from the blacklist — it's protected again.";
    }

    private void ApplyBlacklistChange()
    {
      SaveConfig();
      BuildBlacklist();
      ApplyEngineFlags();

      if (_config.Settings.Enabled)
        FixExistingItems();
    }

    #endregion

    #region Item filter

    private void BuildBlacklist()
    {
      _blacklist = new HashSet<string>(
        _config.Settings.ItemBlacklist
          .Where(s => !string.IsNullOrWhiteSpace(s))
          .Select(s => s.Trim().ToLowerInvariant()));
    }

    private void ValidateBlacklist()
    {
      foreach (var shortname in _config.Settings.ItemBlacklist)
      {
        if (string.IsNullOrWhiteSpace(shortname)) continue;
        if (ItemManager.FindItemDefinition(shortname.Trim()) == null)
          PrintWarning($"ItemBlacklist entry '{shortname}' does not match any known item shortname.");
      }
    }

    private bool IsProtected(ItemDefinition definition)
    {
      if (definition?.shortname == null) return false;

      return !_blacklist.Contains(definition.shortname.ToLowerInvariant());
    }

    #endregion

    #region Engine flags

    private void ApplyEngineFlags()
    {
      foreach (var definition in ItemManager.GetItemDefinitions())
      {
        if (definition?.condition == null) continue;

        definition.condition.maintainMaxCondition =
          _config.Settings.Enabled && !_config.Settings.UsePermission && IsProtected(definition);
      }
    }

    #endregion

    #region Repair hook

    private void OnItemRepair(BasePlayer player, Item item)
    {
      if (!_config.Settings.Enabled || item?.info == null) return;
      if (!IsProtected(item.info)) return;

      if (_config.Settings.UsePermission &&
          (player == null || !permission.UserHasPermission(player.UserIDString, _config.Settings.Permission)))
        return;

      NextTick(() => RestoreMaxCondition(item));
    }

    private void RestoreMaxCondition(Item item)
    {
      if (item?.info?.condition == null) return;
      if (item.conditionNormalized < 1f) return;
      if (item.maxCondition >= item.info.condition.max - ConditionEpsilon) return;

      item.maxCondition = item.info.condition.max;
      item.condition = item.maxCondition;

      item.GetHeldEntity()?.SetFlag(BaseEntity.Flags.Broken, false);
      item.MarkDirty();
    }

    #endregion

    #region Retroactive fix

    private void FixExistingItems()
    {
      var itemsFixed = 0;
      var playersScanned = 0;

      foreach (var player in BasePlayer.activePlayerList)
      {
        if (!PlayerPermitted(player)) continue;
        itemsFixed += FixPlayerInventory(player);
        playersScanned++;
      }

      foreach (var player in BasePlayer.sleepingPlayerList)
      {
        if (!PlayerPermitted(player)) continue;
        itemsFixed += FixPlayerInventory(player);
        playersScanned++;
      }

      var containersScanned = 0;

      if (!_config.Settings.UsePermission)
      {
        foreach (var storage in UnityEngine.Object.FindObjectsOfType<StorageContainer>())
        {
          try
          {
            itemsFixed += FixContainer(storage.inventory);
            containersScanned++;
          }
          catch (Exception ex)
          {
            PrintWarning($"Failed to scan storage container {storage?.PrefabName}: {ex.Message}");
          }
        }

        foreach (var dropped in UnityEngine.Object.FindObjectsOfType<DroppedItemContainer>())
        {
          try
          {
            itemsFixed += FixContainer(dropped.inventory);
            containersScanned++;
          }
          catch (Exception ex)
          {
            PrintWarning($"Failed to scan dropped item container: {ex.Message}");
          }
        }
      }

      if (itemsFixed > 0)
        Puts($"Restored max condition on {itemsFixed} existing item(s) across {playersScanned} player(s) and {containersScanned} world container(s).");
    }

    private bool PlayerPermitted(BasePlayer player)
    {
      if (player?.inventory == null) return false;
      return !_config.Settings.UsePermission || permission.UserHasPermission(player.UserIDString, _config.Settings.Permission);
    }

    private int FixPlayerInventory(BasePlayer player)
    {
      var fixedCount = FixContainer(player.inventory.containerMain);
      fixedCount += FixContainer(player.inventory.containerBelt);
      fixedCount += FixContainer(player.inventory.containerWear);
      return fixedCount;
    }

    private int FixContainer(ItemContainer container)
    {
      if (container?.itemList == null) return 0;

      var fixedCount = 0;
      foreach (var item in container.itemList)
        fixedCount += FixItemRecursive(item);

      return fixedCount;
    }

    private int FixItemRecursive(Item item)
    {
      if (item?.info?.condition == null) return 0;

      var fixedCount = 0;

      var pristineMax = item.info.condition.max;
      if (IsProtected(item.info) && item.maxCondition < pristineMax - ConditionEpsilon)
      {
        var oldMax = item.maxCondition;
        if (oldMax > 0f)
          item.condition = Math.Min(item.condition * (pristineMax / oldMax), pristineMax);

        item.maxCondition = pristineMax;
        item.MarkDirty();
        fixedCount++;
      }

      if (item.contents?.itemList != null)
        foreach (var sub in item.contents.itemList)
          fixedCount += FixItemRecursive(sub);

      return fixedCount;
    }

    #endregion
  }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху