C#:
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
using Oxide.Core;
using Oxide.Core.Configuration;
namespace Oxide.Plugins
{
[Info("Extended Recycler", "beee/The Friendly Chap", "1.2.1")]
[Description("Extend recyclers for personal use with per player limits")]
public class ExtendedRecycler : RustPlugin
{
#region Vars
private const ulong skinID = 1594245394;
private const string prefab = "assets/bundled/prefabs/static/recycler_static.prefab";
private static ExtendedRecycler plugin;
private const string permUse = "extendedrecycler.use";
private const string permUnlimited = "extendedrecycler.unlimited";
RecyclersData recData;
private DynamicConfigFile data;
#endregion
#region Config
private static ConfigData config;
private class ConfigData
{
[JsonProperty(PropertyName = "1.Pickup settings:")]
public OPickup pickup;
[JsonProperty(PropertyName = "2.Craft settings:")]
public OCraft craft;
[JsonProperty(PropertyName = "3.Destroy settings:")]
public ODestroy destroy;
public class OPickup
{
[JsonProperty(PropertyName = "1.Enabled for personal recyclers (placed by player)")]
public bool personal;
[JsonProperty(PropertyName = "2.Check ability to build for pickup")]
public bool privilege;
[JsonProperty(PropertyName = "3.Only owner can pickup")]
public bool onlyOwner;
}
public class OCraft
{
[JsonProperty(PropertyName = "1.Enabled")]
public bool enabled;
[JsonProperty(PropertyName = "2.Cost (shortname - amount):")]
public Dictionary<string, int> cost;
[JsonProperty(PropertyName = "3.Default Balance per Player:")]
public int defaultBalance = 2;
}
public class ODestroy
{
[JsonProperty(PropertyName = "1.Check ground for recyclers (destroy on missing)")]
public bool checkGround;
[JsonProperty(PropertyName = "2.Give item on destroy recycler")]
public bool destroyItem;
[JsonProperty(PropertyName = "3.Effects on destroy recycler")]
public List<string> effects;
}
}
private ConfigData GetDefaultConfig()
{
return new ConfigData
{
pickup = new ConfigData.OPickup
{
personal = false,
privilege = true,
onlyOwner = false
},
craft = new ConfigData.OCraft
{
enabled = true,
cost = new Dictionary<string, int>
{
{"scrap", 500},
{"metal.fragments", 5000},
{"metal.refined", 50},
{"gears", 10}
},
defaultBalance = 2
},
destroy = new ConfigData.ODestroy
{
checkGround = true,
destroyItem = true,
effects = new List<string>
{
"assets/bundled/prefabs/fx/item_break.prefab",
"assets/bundled/prefabs/fx/impacts/stab/rock/stab_rock_01.prefab"
}
}
};
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<ConfigData>();
}
catch
{
LoadDefaultConfig();
}
SaveConfig();
}
protected override void LoadDefaultConfig()
{
PrintError("Configuration file is corrupt(or not exists), creating new one!");
config = GetDefaultConfig();
}
protected override void SaveConfig()
{
Config.WriteObject(config);
}
#endregion
#region Language
private Dictionary<string, string> EN = new Dictionary<string, string>
{
{"Name", "Recycler"},
{"Pickup", "You picked up recycler!"},
{"Receive", "You received recycler!"},
{"Disabled", "Pickup disabled!"},
{"Build", "You must have ability to build to do that!"},
{"Damaged", "Recycler was recently damaged, you can pick it up in next 30s!"},
{"NoCraft", "Craft disabled!"},
{"Owner", "Only owner can pickup recycler!"},
{"Craft", "For craft you need more resources:\n{0}"},
{"Permission", "You need permission to do that!"},
{"Claimed", "You claimed a recycler! your current balance is {0}."},
{"LimitExceeded", "You reached your crafting limit!"},
{"Balance", "Your current balance is {0}."}
};
private void message(BasePlayer player, string key, params object[] args)
{
if (player == null)
{
return;
}
var message = string.Format(lang.GetMessage(key, this, player.UserIDString), args);
player.ChatMessage(message);
}
#endregion
#region Oxide Hooks
private void Loaded()
{
data = Interface.Oxide.DataFileSystem.GetFile("ExtendedRecycler");
}
private void OnEntityBuilt(Planner plan, GameObject go)
{
CheckDeploy(go.ToBaseEntity());
}
private void OnServerInitialized()
{
LoadData();
plugin = this;
lang.RegisterMessages(EN, this);
permission.RegisterPermission(permUse, this);
permission.RegisterPermission(permUnlimited, this);
CheckRecyclers();
}
private void OnHammerHit(BasePlayer player, HitInfo info)
{
CheckHit(player, info?.HitEntity);
}
private void Unload()
{
SaveData();
}
private void OnServerSave() => SaveData();
#endregion
#region Core
private void SpawnRecycler(Vector3 position, Quaternion rotation = default(Quaternion), ulong ownerID = 0)
{
var recycler = GameManager.server.CreateEntity(prefab, position, rotation);
if (recycler == null)
{
return;
}
recycler.skinID = skinID;
recycler.OwnerID = ownerID;
recycler.gameObject.AddComponent<ExtendedRecyclerComponent>();
recycler.Spawn();
}
private void CheckRecyclers()
{
foreach (var recycler in UnityEngine.Object.FindObjectsOfType<Recycler>())
{
if (recycler.OwnerID != 0 && recycler.GetComponent<ExtendedRecyclerComponent>() == null)
{
recycler.gameObject.AddComponent<ExtendedRecyclerComponent>();
}
}
}
private void GiveRecycler(BasePlayer player, bool pickup = false, bool free = false)
{
var item = CreateItem();
if (item != null && player != null)
{
player.GiveItem(item);
if(pickup)
{
message(player, "Pickup");
}
else
{
if(permission.UserHasPermission(player.UserIDString, permUnlimited) || free)
{
message(player, "Receive");
}
else
{
int remainingBalance = SpendFromBalance(player);
message(player, "Claimed", remainingBalance);
}
}
}
}
private void GiveRecycler(Vector3 position)
{
var item = CreateItem();
item?.Drop(position, Vector3.down);
}
private Item CreateItem()
{
var item = ItemManager.CreateByName("box.repair.bench", 1, skinID);
if (item != null)
{
item.name = plugin?.GetRecyclerName();
}
return item;
}
private void CheckDeploy(BaseEntity entity)
{
if (entity == null)
{
return;
}
if (!IsRecycler(entity.skinID))
{
return;
}
var transform = entity.transform;
SpawnRecycler(transform.position, transform.rotation, entity.OwnerID);
entity.transform.position -= new Vector3(0, 3, 0);
entity.SendNetworkUpdate();
timer.Once(1f, () =>
{
if (entity.IsValid() == true && entity.IsDestroyed == false)
{
entity.Kill();
}
});
}
private void CheckHit(BasePlayer player, BaseEntity entity)
{
if (entity == null)
{
return;
}
if (!IsRecycler(entity.skinID))
{
return;
}
timer.Once(1f, () =>
{
if (entity.IsValid() == true)
{
entity.GetComponent<ExtendedRecyclerComponent>()?.TryPickup(player);
}
});
}
[ChatCommand("recycler.craft")]
private void Craft(BasePlayer player)
{
if (CanCraft(player))
{
GiveRecycler(player);
}
}
[ChatCommand("recycler.balance")]
private void Balance(BasePlayer player)
{
if (!permission.UserHasPermission(player.UserIDString, permUse))
{
message(player, "Permission");
return;
}
string balance = GetBalance(player).ToString();
if (permission.UserHasPermission(player.UserIDString, permUnlimited))
{
balance = "Unlimited";
}
message(player, "Balance", balance);
}
private bool CanCraft(BasePlayer player)
{
if (!config.craft.enabled)
{
message(player, "NoCraft");
return false;
}
if (!permission.UserHasPermission(player.UserIDString, permUse))
{
message(player, "Permission");
return false;
}
CheckEntry(player);
if(!permission.UserHasPermission(player.UserIDString, permUnlimited)
&& recData.Players[player.userID].Balance <= 0)
{
message(player, "LimitExceeded");
return false;
}
var recipe = config.craft.cost;
var more = new Dictionary<string, int>();
foreach (var component in recipe)
{
var name = component.Key;
var has = player.inventory.GetAmount(ItemManager.FindItemDefinition(component.Key).itemid);
var need = component.Value;
if (has < component.Value)
{
if (!more.ContainsKey(name))
{
more.Add(name, 0);
}
more[name] += need;
}
}
if (more.Count == 0)
{
foreach (var item in recipe)
{
player.inventory.Take(null, ItemManager.FindItemDefinition(item.Key).itemid, item.Value);
}
return true;
}
else
{
var text = "";
foreach (var item in more)
{
text += $" * {item.Key} x{item.Value}\n";
}
player.ChatMessage(string.Format(lang.GetMessage("Craft", this), text));
return false;
}
}
#endregion
#region Helpers
private string GetRecyclerName()
{
return lang.GetMessage("Name", this);
}
private bool IsRecycler(ulong skin)
{
return skin != 0 && skin == skinID;
}
private void CheckEntry(BasePlayer player)
{
if (!recData.Players.ContainsKey(player.userID))
{
recData.Players.Add(player.userID, new PlayerData
{
DisplayName = player.displayName,
Balance = config.craft.defaultBalance
});
}
}
private void CheckEntry(ulong playerId)
{
if (!recData.Players.ContainsKey(playerId))
{
recData.Players.Add(playerId, new PlayerData
{
DisplayName = covalence.Players.FindPlayerById(playerId.ToString())?.Name ?? "NoName",
Balance = config.craft.defaultBalance
});
}
}
public int SpendFromBalance(BasePlayer player){
CheckEntry(player);
if(permission.UserHasPermission(player.UserIDString, permUnlimited)){ return 999; }
recData.Players[player.userID].Balance -= 1;
if(recData.Players[player.userID].Balance < 0){
recData.Players[player.userID].Balance = 0;
}
return recData.Players[player.userID].Balance;
}
private void SetBalance(BasePlayer player, int newBalance)
{
CheckEntry(player);
recData.Players[player.userID].Balance = newBalance;
}
private int AddBalance(BasePlayer player, int deposit)
{
CheckEntry(player);
recData.Players[player.userID].Balance += deposit;
return recData.Players[player.userID].Balance;
}
private int GetBalance(BasePlayer player)
{
CheckEntry(player);
return recData.Players[player.userID].Balance;
}
#endregion
#region Command
[ConsoleCommand("recycler.give")]
private void Cmd(ConsoleSystem.Arg arg)
{
if (arg.IsAdmin && arg.Args?.Length > 0)
{
var player = BasePlayer.Find(arg.Args[0]) ?? BasePlayer.FindSleeping(arg.Args[0]);
if (player == null)
{
PrintWarning($"We can't find player with that name/ID! {arg.Args[0]}");
return;
}
GiveRecycler(player, false, true);
}
}
[ConsoleCommand("recycler.setbalance")]
private void SetBalanceCMD(ConsoleSystem.Arg arg)
{
if (arg.IsAdmin && arg.Args?.Length > 1)
{
var player = BasePlayer.Find(arg.Args[0]) ?? BasePlayer.FindSleeping(arg.Args[0]);
if (player == null)
{
PrintWarning($"We can't find player with that name/ID! {arg.Args[0]}");
return;
}
int newBalance = 0;
if (!int.TryParse(arg.Args[1], out newBalance))
{
PrintWarning($"{arg.Args[1]} is not a valid number!");
return;
}
SetBalance(player, newBalance);
Puts($"{arg.Args[0]} current balance is " + newBalance + ".");
//player.ChatMessage($"Your current recycler craft balance is " + newBalance + ".");
}
}
[ConsoleCommand("rec_wipe")]
private void ccmdPCWipe(ConsoleSystem.Arg arg)
{
if (arg.Connection != null) return;
recData = new RecyclersData();
Puts("Extended Recycler wiped successfully.");
SaveData();
}
#endregion
#region Data
private void SaveData()
{
data.WriteObject(recData);
}
private void LoadData()
{
try
{
recData = data.ReadObject<RecyclersData>();
}
catch
{
recData = new RecyclersData();
}
}
private class RecyclersData
{
public Dictionary<ulong, PlayerData> Players = new Dictionary<ulong, PlayerData>();
}
private class PlayerData
{
public string DisplayName = string.Empty;
public int Balance = config.craft.defaultBalance;
}
#endregion
#region Scripts
private class ExtendedRecyclerComponent : MonoBehaviour
{
private Recycler recycler;
private void Awake()
{
recycler = GetComponent<Recycler>();
if (config.destroy.checkGround)
{
InvokeRepeating("CheckGround", 5f, 5f);
}
}
private void CheckGround()
{
RaycastHit rhit;
var cast = Physics.Raycast(recycler.transform.position + new Vector3(0, 0.1f, 0), Vector3.down,
out rhit, 4f, LayerMask.GetMask("Terrain", "Construction"));
var distance = cast ? rhit.distance : 3f;
if (distance > 0.2f)
{
GroundMissing();
}
}
private void GroundMissing()
{
recycler.Kill();
if (config.destroy.destroyItem)
{
plugin.GiveRecycler(recycler.transform.position);
}
foreach (var effect in config.destroy.effects)
{
Effect.server.Run(effect, recycler.transform.position);
}
}
public void TryPickup(BasePlayer player)
{
if (config.pickup.personal == false)
{
plugin.message(player, "Disabled");
return;
}
if (config.pickup.privilege && !player.CanBuild())
{
plugin.message(player, "Build");
return;
}
if (config.pickup.onlyOwner && recycler.OwnerID != player.userID)
{
plugin.message(player, "Owner");
return;
}
if (recycler.SecondsSinceDealtDamage < 30f)
{
plugin.message(player, "Damaged");
return;
}
recycler.DropItems();
recycler.Kill();
plugin.GiveRecycler(player, true);
}
public void DoDestroy()
{
Destroy(this);
}
}
#endregion
}
}