C#:
using System.Collections.Generic;
using System.Reflection;
using Rust;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("RotatingPickups", "k1lly0u", "0.1.9", ResourceId = 0)]
class RotatingPickups : RustPlugin
{
#region Fields
StoredData storedData;
private DynamicConfigFile data;
[PluginReference]
Plugin ZoneManager;
static RotatingPickups instance;
private bool initialized;
private Dictionary<string, ZoneInfo> dropZones;
private static readonly Collider[] colBuffer = (Collider[])typeof(Vis).GetField("colBuffer", (BindingFlags.Static | BindingFlags.NonPublic))?.GetValue(null);
#endregion
#region Components
class ItemRotator : MonoBehaviour
{
private WorldItem entity;
private Rigidbody rigidBody;
private bool hasBegun;
private float secsToTake;
void Awake()
{
entity = GetComponent<WorldItem>();
enabled = false;
}
void OnDestroy()
{
if (entity == null) return;
if (rigidBody != null)
{
rigidBody.useGravity = true;
rigidBody.isKinematic = false;
}
}
void FixedUpdate()
{
entity.transform.RotateAround(entity.transform.position, Vector3.up, secsToTake);
entity.transform.hasChanged = true;
}
void OnCollisionEnter(Collision collision)
{
if (hasBegun || collision.gameObject == null) return;
if (collision.gameObject.GetComponent<Terrain>() || collision.gameObject.name.Contains("junk_pile") || collision?.gameObject?.layer == (int)Layer.Construction)
{
hasBegun = true;
Invoke("BeginRotation", instance.configData.TimeBeforeRotation);
}
}
void OnTriggerEnter(Collider col)
{
if (!instance.configData.AllowContactPickup) return;
if (col?.gameObject?.layer != 17) return;
var player = col.GetComponentInParent<BasePlayer>();
if (player != null)
{
Item item = entity.item;
if (item != null)
{
if (player.inventory.containerBelt.itemList.Count < 6 || player.inventory.containerMain.itemList.Count < 24 || (player.inventory.containerWear.itemList.Count < 6 && item.info.category == ItemCategory.Attire))
{
player.GiveItem(item, BaseEntity.GiveItemReason.PickedUp);
Destroy(this);
}
}
}
}
private void BeginRotation()
{
gameObject.layer = (int)Layer.Reserved1;
rigidBody = entity.GetComponent<Rigidbody>();
rigidBody.useGravity = false;
rigidBody.isKinematic = true;
rigidBody.detectCollisions = true;
rigidBody.collisionDetectionMode = CollisionDetectionMode.Discrete;
var collider = gameObject.AddComponent<SphereCollider>();
collider.isTrigger = true;
collider.radius = instance.configData.ColliderTriggerRadius;
string shortname = entity.item.info.shortname;
secsToTake = instance.configData.RotationSpeed;
entity.transform.transform.eulerAngles = new Vector3(instance.rotateList.ContainsKey(shortname) ? instance.rotateList[shortname] : 0, 0, 0);
entity.transform.position = entity.transform.position + (shortname.Contains("spear") ? Vector3.up * 2 : Vector3.up);
enabled = true;
}
}
class ZoneInfo
{
public Vector3 Position;
public float Radius;
public bool IsBox;
public Vector3 Size;
public Vector3 Rotation;
}
#endregion
#region Oxide Hooks
void Loaded()
{
data = Interface.Oxide.DataFileSystem.GetFile("rotatingpickups_zonelist");
dropZones = new Dictionary<string, ZoneInfo>();
permission.RegisterPermission("rotatingpickups.zones", this);
}
void OnServerInitialized()
{
initialized = true;
instance = this;
LoadVariables();
LoadData();
if (configData.AllowInZonesOnly)
FindAllZones();
}
void OnEntitySpawned(BaseNetworkable entity)
{
if (!initialized) return;
WorldItem worldItem = entity.GetComponent<WorldItem>();
if (worldItem != null)
{
if (!IsInZone(worldItem)) return;
Item item = worldItem.item;
if (item != null)
{
if (configData.TypesToRotate.ContainsKey(item.info.category))
{
if (configData.TypesToRotate[item.info.category])
{
NextTick(() =>
{
if (worldItem != null)
worldItem.gameObject.AddComponent<ItemRotator>();
});
}
}
}
}
}
void Unload()
{
var objects = UnityEngine.Object.FindObjectsOfType<ItemRotator>();
if (objects != null)
foreach (var obj in objects)
UnityEngine.Object.Destroy(obj);
}
#endregion
#region Functions
void FindAllZones()
{
if (!ZoneManager) return;
foreach(var zoneId in storedData.zoneIds)
{
GetZoneInfo(zoneId);
}
}
void GetZoneInfo(string zoneId)
{
object success = ZoneManager?.Call("GetZoneLocation", zoneId);
if (success is Vector3)
{
Vector3 position = (Vector3)success;
success = ZoneManager?.Call("GetZoneSize", zoneId);
if (success is Vector3 && (Vector3)success != Vector3.zero)
{
Vector3 size = (Vector3)success;
success = ZoneManager.Call("ZoneFieldList", zoneId);
if (success == null) return;
Dictionary<string, string> fieldList = (Dictionary<string, string>)success;
if (fieldList.ContainsKey("rotation"))
{
string rotString = fieldList["rotation"];
dropZones.Add(zoneId, new ZoneInfo { Position = position, IsBox = true, Size = size, Rotation = ParseV3String(rotString) });
}
return;
}
else success = ZoneManager?.Call("GetZoneRadius", zoneId);
if (success is float && (float)success != 0)
dropZones.Add(zoneId, new ZoneInfo { Position = position, Radius = (float)success });
}
}
Vector3 ParseV3String(string source)
{
string outString = source.Substring(1, source.Length - 2);
string[] splitString = outString.Split("," [0]);
return new Vector3(float.Parse(splitString[0]), float.Parse(splitString[1]), float.Parse(splitString[2]));
}
bool IsInZone(WorldItem entity)
{
if (!configData.AllowInZonesOnly) return true;
foreach(var zone in dropZones)
{
if (!zone.Value.IsBox)
{
if (Vector3.Distance(zone.Value.Position, entity.transform.position) < zone.Value.Radius)
return true;
}
else
{
int entities = 0;
if (zone.Value.Size != Vector3.zero)
entities = Physics.OverlapBoxNonAlloc(zone.Value.Position, new Vector3(zone.Value.Size.x /2, zone.Value.Size.y / 2, zone.Value.Size.z / 2), colBuffer, Quaternion.Euler(zone.Value.Rotation));
for (var i = 0; i < entities; i++)
{
var item = colBuffer[i].GetComponentInParent<WorldItem>();
colBuffer[i] = null;
if (item != null)
{
if (item == entity) return true;
}
}
}
}
return false;
}
#endregion
#region Commands
[ChatCommand("dropzone")]
void cmdDropZone(BasePlayer player, string command, string[] args)
{
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, "rotatingpickups.zones")) return;
if (!ZoneManager)
{
SendReply(player, "ZoneManager is not installed! Unable to register zones for item drops");
return;
}
if (args.Length == 1 && args[0].ToLower() == "list")
{
string zoneList = "--- Registered Zone IDs ---";
foreach (var zoneId in storedData.zoneIds)
zoneList += $"\n{zoneId}";
SendReply(player, zoneList);
return;
}
if (args.Length != 2)
{
SendReply(player, $"{Title} v.{Version} -- Registered Zones");
SendReply(player, "/dropzone add <zoneid> - Adds the zone ID to the list zones");
SendReply(player, "/dropzone remove <zoneid> - Removes the zone ID from the list of zones");
SendReply(player, "/dropzone list - List all registered zone IDs");
return;
}
switch (args[0].ToLower())
{
case "add":
object success = ZoneManager?.Call("CheckZoneID", args[1]);
if (success != null)
{
storedData.zoneIds.Add(args[1]);
GetZoneInfo(args[1]);
SaveData();
SendReply(player, "You have successfully added this zone to the zone list");
return;
}
else SendReply(player, "That is a invalid Zone ID");
return;
case "remove":
if (storedData.zoneIds.Contains(args[1]))
{
if (dropZones.ContainsKey(args[1]))
dropZones.Remove(args[1]);
storedData.zoneIds.Remove(args[1]);
SaveData();
SendReply(player, "You have successfully removed this zone from the zone list");
return;
}
else SendReply(player, "The specified zone ID is not in the list of registered zones");
return;
default:
break;
}
}
[ConsoleCommand("dropzone")]
void ccmdDropZone(ConsoleSystem.Arg arg)
{
if (arg.Connection != null) return;
if (!ZoneManager)
{
SendReply(arg, "ZoneManager is not installed! Unable to register zones for item drops");
return;
}
if (arg.Args.Length == 1 && arg.Args[0].ToLower() == "list")
{
string zoneList = "--- Registered Zone IDs ---";
foreach (var zoneId in storedData.zoneIds)
zoneList += $"\n{zoneId}";
SendReply(arg, zoneList);
return;
}
if (arg.Args.Length != 2)
{
SendReply(arg, $"{Title} v.{Version} -- Registered Zones");
SendReply(arg, "dropzone add <zoneid> - Adds the zone ID to the list zones");
SendReply(arg, "dropzone remove <zoneid> - Removes the zone ID from the list of zones");
SendReply(arg, "dropzone list - List all registered zone IDs");
return;
}
switch (arg.Args[0].ToLower())
{
case "add":
object success = ZoneManager?.Call("CheckZoneID", arg.Args[1]);
if (success != null)
{
storedData.zoneIds.Add(arg.Args[1]);
GetZoneInfo(arg.Args[1]);
SaveData();
SendReply(arg, "You have successfully added this zone to the zone list");
return;
}
else SendReply(arg, "That is a invalid Zone ID");
return;
case "remove":
if (storedData.zoneIds.Contains(arg.Args[1]))
{
if (dropZones.ContainsKey(arg.Args[1]))
dropZones.Remove(arg.Args[1]);
storedData.zoneIds.Remove(arg.Args[1]);
SaveData();
SendReply(arg, "You have successfully removed this zone from the zone list");
return;
}
else SendReply(arg, "The specified zone ID is not in the list of registered zones");
return;
default:
break;
}
}
#endregion
#region Config
private ConfigData configData;
class ConfigData
{
public bool AllowContactPickup { get; set; }
public bool AllowInZonesOnly { get; set; }
public float RotationSpeed { get; set; }
public float ColliderTriggerRadius { get; set; }
public int TimeBeforeRotation { get; set; }
public Dictionary<ItemCategory, bool> TypesToRotate { get; set; }
}
private void LoadVariables()
{
LoadConfigVariables();
SaveConfig();
}
protected override void LoadDefaultConfig()
{
var config = new ConfigData
{
AllowContactPickup = true,
AllowInZonesOnly = true,
ColliderTriggerRadius = 0.5f,
RotationSpeed = 5f,
TimeBeforeRotation = 1,
TypesToRotate = new Dictionary<ItemCategory, bool>
{
{ItemCategory.Ammunition, true },
{ItemCategory.Attire, true },
{ItemCategory.Component, false },
{ItemCategory.Construction, false },
{ItemCategory.Food, true },
{ItemCategory.Items, false },
{ItemCategory.Medical, true },
{ItemCategory.Misc, false },
{ItemCategory.Resources, true },
{ItemCategory.Tool, true },
{ItemCategory.Traps, false },
{ItemCategory.Weapon, true },
{ItemCategory.Common, false }
}
};
SaveConfig(config);
}
private void LoadConfigVariables() => configData = Config.ReadObject<ConfigData>();
void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
#endregion
#region Data Management
void SaveData() => data.WriteObject(storedData);
void LoadData()
{
try
{
storedData = data.ReadObject<StoredData>();
}
catch
{
storedData = new StoredData();
}
}
class StoredData
{
public List<string> zoneIds = new List<string>();
}
#endregion
Dictionary<string, int> rotateList = new Dictionary<string, int>
{
{ "knife.bone", 270 },
{ "rifle.ak", 90 },
{ "smg.2", 270 },
{ "shotgun.double", 90 },
{ "spear.wooden", 270 },
{ "spear.stone", 270 },
{ "smg.thompson", 270 },
{ "shotgun.waterpipe", 90 },
{ "building.planner", 270 },
{ "map", 270 },
{ "mask.bandana", 270 },
{ "hat.beenie", 270 },
{ "coffeecan.helmet", 270 },
{ "hammer.salvaged", 270 },
{ "axe.salvaged", 270 },
};
}
}