C#:
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using UnityEngine;
using Oxide.Core;
using Oxide.Core.Plugins;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Facepunch;
using Rust;
#region Changelogs and ToDo
/**********************************************************************
* 1.4.0 - Optimised the skull and bone mechanics
* - Added API private void OnAnimalSkullCrushReward(BasePlayer player , string itemName , ulong itemId , int amount) returning info
* - Added API private void OnAnimalBonesReceived(BasePlayer player, string itemName , int amount , ulong skinId) returning info
* 1.4.1 - Added the new Animals including snake.
* - Fixed snake corpse not being a real corpse.
- Added snake methods for harvesting.
- Several skins added/changed.
- Made bone skins configurable again.
- Added Bone Powder (Glue) when unsuccesfull crush of a skull (outside inventory)
- Use StackModifier(Umod) or SplitFix (codefling) for splitting issues
*
* API :
private void OnAnimalSkullCrushReward(BasePlayer player , string itemName , ulong itemId , int amount)
{
Puts($"{player.displayName} received {amount} {itemName} with skin ID {itemId} from a skull crush!");
}
private void OnAnimalBonesReceived(BasePlayer player, string itemName , int amount , ulong skinId)
{
Puts($"{player.displayName} Received {amount} of {itemName} with skin ID: {skinId}");
}
**********************************************************************/
#endregion
namespace Oxide.Plugins
{
[Info("SkinAndBones", "Krungh Crow", "1.4.1")]
[Description("Core item Injection for animals")]
class SkinAndBones : RustPlugin
{
#region Variables
ulong chaticon = 0;
string prefix;
bool Debug = false;
bool CanConvertBones = true;
const string Admin_Perm = "skinandbones.admin";
private readonly HashSet<NetworkableId> SnakeCorpse = new HashSet<NetworkableId>();
#region Bone Skin Id's
ulong SkinBoneBe;
ulong SkinBoneBo;
ulong SkinBoneCh;
ulong SkinBoneSh;
ulong SkinBoneHo;
ulong SkinBoneWo;
ulong SkinBoneSt;
ulong SkinBoneFe;
ulong SkinBoneCr;
ulong SkinBoneSn;
#endregion
#region Bone Names
const string BoneNameBe = "Bear Bones";
const string BoneNameBo = "Boar Bones";
const string BoneNameCh = "Chicken Bones";
const string BoneNameHo = "Horse Bones";
const string BoneNameSt = "Stag Bones";
const string BoneNameSh = "Fish Bones";
const string BoneNameWo = "Wolf Bones";
const string BoneNameFe = "Feline Bones";
const string BoneNameCr = "Crocodile Bones";
const string BoneNameSn = "Snake Bones";
#endregion
#region Skull skin Id's
ulong SkinSkullBe;
ulong SkinSkullBo;
ulong SkinSkullCh;
ulong SkinSkullSh;
ulong SkinSkullHo;
ulong SkinSkullWo;
ulong SkinSkullSt;
ulong SkinSkullFe;
ulong SkinSkullCr;
ulong SkinSkullSn;
#endregion
#region Skullcrush Amounts
int CrushAmountBe;
int CrushAmountBo;
int CrushAmountCh;
int CrushAmountSh;
int CrushAmountHo;
int CrushAmountSt;
int CrushAmountWo;
int CrushAmountFe;
int CrushAmountCr;
int CrushAmountSn;
#endregion
#region Skull Names
const string SkullBe = "Bear Skull";
const string SkullBo = "Boar Skull";
const string SkullCh = "Chicken Skull";
const string SkullSh = "Shark jaws";
const string SkullHo = "Horse Skull";
const string SkullSt = "Stag Skull";
const string SkullFe = "Feline Skull";
const string SkullCr = "Crocodile Skull";
const string SkullSn = "Snake Fangs";
#endregion
#endregion
#region Init
void Init()
{
if (!LoadConfigVariables())
{
Puts("Config file issue detected. Please delete file, or check syntax and fix.");
return;
}
Debug = configData.PlugCFG.Debug;
prefix = "[<color=green>Skin & Bones</color>] ";
chaticon = 0;
CanConvertBones = configData.CanConvert;
CheckSubscriptions();
#region Skull Skin Id's assign
SkinSkullBe = configData.SkullId.Bear;
SkinSkullBo = configData.SkullId.Boar;
SkinSkullCh = configData.SkullId.Chicken;
SkinSkullSh = configData.SkullId.Shark;
SkinSkullHo = configData.SkullId.Horse;
SkinSkullSt = configData.SkullId.Stag;
SkinSkullFe = configData.SkullId.Feline;
SkinSkullCr = configData.SkullId.Croc;
SkinSkullSn = configData.SkullId.Snake;
#endregion
#region Bones Skin Id's assign
SkinBoneBe = configData.BoneId.Bear;
SkinBoneBo = configData.BoneId.Boar;
SkinBoneCh = configData.BoneId.Chicken;
SkinBoneSh = configData.BoneId.Shark;
SkinBoneHo = configData.BoneId.Horse;
SkinBoneSt = configData.BoneId.Stag;
SkinBoneFe = configData.BoneId.Feline;
SkinBoneCr = configData.BoneId.Croc;
SkinBoneSn = configData.BoneId.Snake;
#endregion
#region Skullcrush Amounts
CrushAmountBe = configData.Crushing.Bear;
CrushAmountBo = configData.Crushing.Boar;
CrushAmountCh = configData.Crushing.Chicken;
CrushAmountSh = configData.Crushing.Shark;
CrushAmountHo = configData.Crushing.Horse;
CrushAmountSt = configData.Crushing.Stag;
CrushAmountWo = configData.Crushing.Wolf;
CrushAmountFe = configData.Crushing.Feline;
CrushAmountCr = configData.Crushing.Croc;
CrushAmountSn = configData.Crushing.Snake;
#endregion
if (Debug) Puts($"[Debug] Debug is activated if unintentional disable this in the config !");
}
#endregion
#region Configuration
private ConfigData configData;
class ConfigData
{
[JsonProperty(PropertyName = "Main config")]
public SettingsPlugin PlugCFG = new SettingsPlugin();
[JsonProperty(PropertyName = "Convert Bones")]
public bool CanConvert = true;
[JsonProperty(PropertyName = "FX on Fail Crush")]
public string FX = "assets/prefabs/misc/halloween/skull_door_knocker/effects/door_knock_fx.prefab";
[JsonProperty(PropertyName = "Bones From Skullcrushing")]
public SettingsCrush Crushing = new SettingsCrush();
[JsonProperty(PropertyName = "SkinID's Bones")]
public SettingsBoneID BoneId = new SettingsBoneID();
[JsonProperty(PropertyName = "SkinID's Skulls")]
public SettingsSkullID SkullId = new SettingsSkullID();
}
class SettingsPlugin
{
[JsonProperty(PropertyName = "Debug")]
public bool Debug = true;
}
class SettingsCrush
{
[JsonProperty(PropertyName = "Bear Bones")]
public int Bear = 30;
[JsonProperty(PropertyName = "Boar Bones")]
public int Boar = 15;
[JsonProperty(PropertyName = "Chicken Bones")]
public int Chicken = 10;
[JsonProperty(PropertyName = "Horse Bones")]
public int Horse = 25;
[JsonProperty(PropertyName = "Shark (fish)Bones")]
public int Shark = 40;
[JsonProperty(PropertyName = "Stag Bones")]
public int Stag = 15;
[JsonProperty(PropertyName = "Wolf Bones")]
public int Wolf = 20;
[JsonProperty(PropertyName = "Feline Bones")]
public int Feline = 25;
[JsonProperty(PropertyName = "Crocodile Bones")]
public int Croc = 40;
[JsonProperty(PropertyName = "Snake Bones")]
public int Snake = 10;
}
class SettingsBoneID
{
[JsonProperty(PropertyName = "Bear Bones SkinID")]
public ulong Bear = 2582006231;
[JsonProperty(PropertyName = "Boar Bones SkinID")]
public ulong Boar = 2582018117;
[JsonProperty(PropertyName = "Chicken Bones SkinID")]
public ulong Chicken = 2582002045;
[JsonProperty(PropertyName = "Horse Bones SkinID")]
public ulong Horse = 2582022311;
[JsonProperty(PropertyName = "Shark Bones SkinID")]
public ulong Shark = 2583801321;
[JsonProperty(PropertyName = "Stag Bones SkinID")]
public ulong Stag = 2582020463;
[JsonProperty(PropertyName = "Wolf Bones SkinID")]
public ulong Wolf = 2582021640;
[JsonProperty(PropertyName = "Feline (panther/tiger) Bones SkinID")]
public ulong Feline = 3531044884;
[JsonProperty(PropertyName = "Crocodile Bones SkinID")]
public ulong Croc = 3531046639;
[JsonProperty(PropertyName = "Snake Bones SkinID")]
public ulong Snake = 3531103631;
}
class SettingsSkullID
{
[JsonProperty(PropertyName = "Bear Skull SkinID")]
public ulong Bear = 2582058305;
[JsonProperty(PropertyName = "Boar Skull SkinID")]
public ulong Boar = 2582176436;
[JsonProperty(PropertyName = "Chicken Skull SkinID")]
public ulong Chicken = 2582166639;
[JsonProperty(PropertyName = "Horse Skull SkinID")]
public ulong Horse = 2582200089;
[JsonProperty(PropertyName = "Shark Skull SkinID")]
public ulong Shark = 2583809564;
[JsonProperty(PropertyName = "Stag Skull SkinID")]
public ulong Stag = 2582184045;
[JsonProperty(PropertyName = "Feline (panther/tiger) Skull SkinID")]
public ulong Feline = 3476574722;
[JsonProperty(PropertyName = "Crocodile Skull SkinID")]
public ulong Croc = 3476568857;
[JsonProperty(PropertyName = "Snake fangs SkinID")]
public ulong Snake = 3531038249;
}
private bool LoadConfigVariables()
{
try
{
configData = Config.ReadObject<ConfigData>();
}
catch
{
return false;
}
SaveConf();
return true;
}
protected override void LoadDefaultConfig()
{
Puts("Fresh install detected Creating a new config file.");
configData = new ConfigData();
SaveConf();
}
void SaveConf() => Config.WriteObject(configData, true);
#endregion
#region LanguageAPI
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["CommandBones"] = "<color=4A95CC>/bones {animalname} {amount}</color> : Gives the animals bones and amounts\n(bear,boar,chicken,fish,horse,stag,wolf)",
["CommandSkull"] = "<color=4A95CC>/skull {animalname} {amount}</color> : Gives the animals skull and amounts\n(bear,boar,chicken,shark,horse,stag,wolf)",
["CrushMessage"] = "</color>You crushed a <color=#4A95CC>{0}</color> into {1} <color=#4A95CC>{2}</color>",
["CrushMessageFail"] = "You failed crushing the <color=#4A95CC>{0}</color>.\nTry crushing them from your inventory",
["GiveMessage"] = "You where given <color=#4A95CC>{0}x {1}</color>",
["GiveErrorAmount"] = "Value needs to be higher then 0",
["GiveErrorBones"] = "The server uses normal :bone.fragments: bone.fragments",
["InvalidInput"] = "<color=red>Please enter a valid command!</color>",
["NoPermission"] = "<color=green>You do not have permission to use that command!</color>",
}, this);
}
#endregion
#region Commands
[ChatCommand("bones")]
private void cmdBones(BasePlayer player, string command, string[] args)
{
if (!HasPerm(player, Admin_Perm))
{
Player.Message(player, prefix + string.Format(msg("NoPermission", player.UserIDString)), chaticon);
return;
}
int amount;
if (args.Length == 0 || (args.Length != 2 && args.Length != 3))
{
Player.Message(player, prefix + string.Format(msg("InvalidInput", player.UserIDString)), chaticon);
return;
}
int.TryParse(args[1], out amount);
if (amount <= 0)
{
Player.Message(player, prefix + string.Format(msg("GiveErrorAmount", player.UserIDString)), chaticon);
return;
}
if (CanConvertBones == false)
{
Player.Message(player, prefix + string.Format(msg("GiveErrorBones", player.UserIDString)), chaticon);
return;
}
else
{
switch (args[0].ToLower())
{
case "bear":
GiveSkinnedBones(player , amount , BoneNameBe , SkinBoneBe);
break;
case "boar":
GiveSkinnedBones(player , amount , BoneNameBo , SkinBoneBo);
break;
case "chicken":
GiveSkinnedBones(player , amount , BoneNameCh , SkinBoneCh);
break;
case "horse":
GiveSkinnedBones(player , amount , BoneNameHo , SkinBoneHo);
break;
case "stag":
GiveSkinnedBones(player , amount , BoneNameSt , SkinBoneSt);
break;
case "fish":
GiveSkinnedBones(player , amount , BoneNameSh , SkinBoneSh);
break;
case "wolf":
GiveSkinnedBones(player , amount , BoneNameWo , SkinBoneWo);
break;
case "panther":
case "tiger":
GiveSkinnedBones(player , amount , BoneNameFe , SkinBoneFe);
break;
case "crocodile":
GiveSkinnedBones(player , amount , BoneNameCr , SkinBoneCr);
break;
case "snake":
GiveSkinnedBones(player , amount , BoneNameSn , SkinBoneSn);
break;
default:
Player.Message(player , prefix + string.Format(msg("InvalidInput" , player.UserIDString)) , chaticon);
break;
}
}
}
[ChatCommand("skull")]
private void cmdSkull(BasePlayer player, string command, string[] args)
{
if (!HasPerm(player, Admin_Perm))
{
Player.Message(player, prefix + string.Format(msg("NoPermission", player.UserIDString)), chaticon);
return;
}
int amount;
if (args.Length == 0 || (args.Length != 2 && args.Length != 3))
{
Player.Message(player, prefix + string.Format(msg("InvalidInput", player.UserIDString)), chaticon);
return;
}
int.TryParse(args[1], out amount);
if (amount <= 0)
{
Player.Message(player, prefix + string.Format(msg("GiveErrorAmount", player.UserIDString)), chaticon);
return;
}
else
{
switch (args[0].ToLower())
{
case "bear":
GiveSkinnedSkull(player , amount , SkullBe , SkinSkullBe);
break;
case "boar":
GiveSkinnedSkull(player , amount , SkullBo , SkinSkullBo);
break;
case "chicken":
GiveSkinnedSkull(player , amount , SkullCh , SkinSkullCh);
break;
case "horse":
GiveSkinnedSkull(player , amount , SkullHo , SkinSkullHo);
break;
case "stag":
GiveSkinnedSkull(player , amount , SkullSt , SkinSkullSt);
break;
case "shark":
GiveSkinnedSkull(player , amount , SkullSh , SkinSkullSh);
break;
case "wolf":
GiveSkinnedSkull(player , amount , null , 0);
break;
case "panther":
case "tiger":
GiveSkinnedSkull(player , amount , SkullFe , SkinSkullFe);
break;
case "crocodile":
GiveSkinnedSkull(player , amount , SkullCr , SkinSkullCr);
break;
case "snake":
GiveSkinnedSkull(player , amount , SkullSn , SkinSkullSn);
break;
default:
Player.Message(player , prefix + string.Format(msg("InvalidInput" , player.UserIDString)) , chaticon);
break;
}
}
}
#endregion
#region Helpers
bool HasPerm(BasePlayer player, string perm) { return permission.UserHasPermission(player.UserIDString, perm); }
void CheckSubscriptions()
{
permission.RegisterPermission(Admin_Perm, this);
timer.Once(3f, () =>
{
if (configData.CanConvert == false)
{
Unsubscribe(nameof(OnItemAction));
if (Debug) Puts($"Unsubscribed to OnItemAction");
}
Puts("Checked Subscriptions");
});
}
void ConvertItem(BasePlayer player, Item item , string _name , ulong _id)
{
item.name = _name;
item.skin = _id;
item.MarkDirty();
if (Debug) Puts($"{player} Converted {item.amount} [{_name}]");
Interface.CallHook("OnAnimalBonesReceived", player , _name , item.amount, _id);
}
void ConvertCrush(BasePlayer player, Item item, int _amount, string _name, ulong _id)
{
if(item.GetRootContainer() != player.inventory.containerMain) return;
item.UseItem();
Item bones = ItemManager.CreateByName("bone.fragments", 1);
bones.amount = _amount;
bones.name = _name;
bones.skin = _id;
bones.MarkDirty();
player.inventory.GiveItem(bones, null);
Interface.CallHook("OnAnimalSkullCrushReward" , player , _name , _id , _amount);
if (Debug) Puts($"{player.displayName} crushed a {item.name} into X{_amount}[{bones.name}]");
Player.Message(player, prefix + string.Format(msg($"CrushMessage", player.UserIDString), item.name, _amount, _name), chaticon);
}
void GiveSkinnedBones(BasePlayer player, int _amount, string _name, ulong _id)//Bones
{
Item bones = ItemManager.CreateByName("bone.fragments", 1);
bones.amount = _amount;
bones.name = _name;
bones.skin = _id;
bones.MarkDirty();
player.inventory.GiveItem(bones, null);
if (Debug) Puts($"{player.displayName} was given {_amount}x {bones.name}");
Player.Message(player, prefix + string.Format(msg($"GiveMessage", player.UserIDString),_amount, bones.name), chaticon);
}
void GiveSkinnedSkull(BasePlayer player, int _amount, string _name, ulong _id)//skulls
{
Item bones = ItemManager.CreateByName("skull.wolf", 1);
bones.amount = _amount;
bones.name = _name;
bones.skin = _id;
bones.MarkDirty();
player.inventory.GiveItem(bones, null);
if (Debug) Puts($"{player.displayName} was given {_amount}x {bones.name}");
Player.Message(player, prefix + string.Format(msg($"GiveMessage", player.UserIDString), _amount, bones.name), chaticon);
}
private void GiveSnakeHarvest(BasePlayer player , BaseEntity dispenser)
{
if (dispenser == null || player == null) return;
if (SnakeCorpse.Contains(dispenser.net.ID))
return;
SnakeCorpse.Add(dispenser.net.ID);
Item item = ItemManager.CreateByName("skull.wolf" , 1 , SkinSkullSn);
if (item != null)
{
item.name = SkullSn;
item.MarkDirty();
player.GiveItem(item);
}
Item item2 = ItemManager.CreateByName("bone.fragments" , 5 , SkinBoneSn);
if (item2 != null)
{
item2.name = BoneNameSn;
item2.MarkDirty();
player.GiveItem(item2);
}
}
#endregion
#region Hooks
void OnEntitySpawned(BaseCorpse corpse)
{
if (corpse == null || corpse.IsDestroyed) return;
var dispenser = corpse.GetComponent<ResourceDispenser>();
if (dispenser == null) return;
var validCorpses = new List<string>
{
"bear.corpse", "boar.corpse", "chicken.corpse",
"horse.corpse", "shark.corpse", "stag.corpse","snake.entity",
"panther.corpse","tiger.corpse","crocodile.corpse"
};
if (validCorpses.Any(corpseName => corpse.ToString().Contains(corpseName)))
{
Item skull = ItemManager.CreateByName("skull.wolf" , 1 , 0);
if (Debug) Puts($"A {corpse} was injected with a Skull");
ItemAmount SkullInfo = new ItemAmount() { itemDef = skull.info , amount = 1 , startAmount = 1 };
dispenser.containedItems.Add(SkullInfo);
dispenser.Initialize();
}
}
private Dictionary<string , (string BoneName, ulong BoneSkin, string SkullName, ulong SkullSkin)> AnimalSkinMap;
void InitializeAnimalSkinMap()
{
AnimalSkinMap = new Dictionary<string , (string, ulong, string, ulong)>
{
{ "bear.corpse", (BoneNameBe, SkinBoneBe, SkullBe, SkinSkullBe) },
{ "polarbear.corpse", (BoneNameBe, SkinBoneBe, SkullBe, SkinSkullBe) },
{ "boar.corpse", (BoneNameBo, SkinBoneBo, SkullBo, SkinSkullBo) },
{ "chicken.corpse", (BoneNameCh, SkinBoneCh, SkullCh, SkinSkullCh) },
{ "horse.corpse", (BoneNameHo, SkinBoneHo, SkullHo, SkinSkullHo) },
{ "shark.corpse", (BoneNameSh, SkinBoneSh, SkullSh , SkinSkullSh) },
{ "stag.corpse", (BoneNameSt, SkinBoneSt, SkullSt, SkinSkullSt) },
{ "wolf.corpse", (BoneNameWo, SkinBoneWo, null, 0) }, // Only bone, no skull
{ "panther.corpse", (BoneNameFe, SkinBoneFe, SkullFe, SkinSkullFe) },
{ "tiger.corpse", (BoneNameFe, SkinBoneFe, SkullFe, SkinSkullFe) },
{ "snake.entity", (BoneNameSn , SkinBoneSn, SkullSn, SkinSkullSn) },
{ "crocodile.corpse", (BoneNameCr, SkinBoneCr, SkullCr, SkinSkullCr) }
};
}
void OnServerInitialized()
{
InitializeAnimalSkinMap();
}
void OnDispenserGather(ResourceDispenser dispenser , BaseEntity entity , Item item)
{
BasePlayer player = entity.ToPlayer();
if (player == null || dispenser == null || item == null) return;
string dispenserKey = dispenser.gameObject.ToBaseEntity()?.ShortPrefabName;
if (AnimalSkinMap.TryGetValue(dispenserKey , out var skinData))
{
if (item.info.itemid == 1719978075 && CanConvertBones)
{
ConvertItem(player , item , skinData.BoneName , skinData.BoneSkin);
}
else if (item.info.itemid == 2048317869 && skinData.SkullName != null)
{
ConvertItem(player , item , skinData.SkullName , skinData.SkullSkin);
}
}
var prefab = dispenser?.gameObject?.GetComponent<BaseEntity>()?.ShortPrefabName?.ToLower();
if (!string.IsNullOrEmpty(prefab) && prefab.Contains("snake"))
{
var sourceEntity = dispenser.gameObject?.GetComponent<BaseEntity>();
GiveSnakeHarvest(player , sourceEntity);
}
}
private object OnItemAction(Item item, string action, BasePlayer player)
{
BasePlayer ownerPlayer = item.GetOwnerPlayer();
if (action == "crush")
{
var crushData = new Dictionary<string , (int amount, string boneName, ulong skin)>
{
{ "wolf skull", (CrushAmountWo, BoneNameWo, SkinBoneWo) }, // default
{ "bear skull", (CrushAmountBe, BoneNameBe, SkinBoneBe) },
{ "boar skull", (CrushAmountBo, BoneNameBo, SkinBoneBo) },
{ "chicken skull", (CrushAmountCh, BoneNameCh, SkinBoneCh) },
{ "horse skull", (CrushAmountHo, BoneNameHo, SkinBoneHo) },
{ "stag skull", (CrushAmountSt, BoneNameSt, SkinBoneSt) },
{ "shark jaws", (CrushAmountSh, BoneNameSh, SkinBoneSh) },
{ "feline skull", (CrushAmountFe, BoneNameFe, SkinBoneFe) },
{ "crocodile skull", (CrushAmountCr, BoneNameCr, SkinBoneCr) },
{ "snake fangs", (CrushAmountSn, BoneNameSn, SkinBoneSn) },
};
try
{
if (item.info.shortname == "skull.wolf")
{
if (item.name == null)
{
item.name = "Wolf Skull";
}
string lowerName = item.name.ToLower();
foreach (var entry in crushData)
{
if (lowerName.Contains(entry.Key))
{
ConvertCrush(ownerPlayer , item , entry.Value.amount , entry.Value.boneName , entry.Value.skin);
return true;
}
}
}
}
catch
{
if (item.skin == 0) return null;
if (Debug) Puts($"A {item.name} was crushed but the bone extraction was unsuccessful");
Player.Message(player , prefix + string.Format(msg("CrushMessageFail" , player.UserIDString) , item.name) , chaticon);
Effect.server.Run(configData.FX , player.transform.position);
Item fallbackBone = ItemManager.CreateByName("glue" , 1 , 3531160322);
if (fallbackBone != null)
{
fallbackBone.name = "Bone Powder";
fallbackBone.MarkDirty();
player.GiveItem(fallbackBone);
item.UseItem();
}
return true;
}
}
return null;
}
#endregion
#region Message helper
private string msg(string key, string id = null) => lang.GetMessage(key, this, id);
#endregion
}
}