Skip to content

Instantly share code, notes, and snippets.

@SpencerSharkey
Created October 7, 2020 08:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SpencerSharkey/b33f8537329fc2edd4d36796eef78e9d to your computer and use it in GitHub Desktop.
Save SpencerSharkey/b33f8537329fc2edd4d36796eef78e9d to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("NPC Drop Gun", "2CHEVSKII", "2.0.4")]
[Description("Forces NPC to drop used gun and other items after death")]
internal class NPCDropGun : RustPlugin
{
#region Fields
Settings settings;
Dictionary<ulong, List<Item>> delayedItems;
#endregion
#region Oxide hooks
void Init() => delayedItems = new Dictionary<ulong, List<Item>>();
void OnServerInitialized()
{
// Warn if BotSpawn may cause unexpected behaviour
if ((bool)Manager.GetPlugin("BotSpawn"))
PrintWarning("BotSpawn plugin found! Some ammo and loot might not be handled correctly!");
}
void OnEntityDeath(BasePlayer player)
{
if (player is HTNPlayer || player is NPCPlayer)
{
DoSpawns(player);
}
}
void OnEntitySpawned(NPCPlayerCorpse corpse)
{
timer.Once(0.1f, () => {
var steamID = corpse.playerSteamID;
if (!corpse || !delayedItems.ContainsKey(steamID))
{
return;
}
if (settings.RemoveDefault)
{
for (int i = 0; i < corpse.containers.Length; i++)
{
corpse.containers[i].Clear();
}
}
var list = delayedItems[steamID];
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
if (!item.MoveToContainer(corpse.containers[2]) && !item.MoveToContainer(corpse.containers[0]))
{
ApplyVelocity(DropNearPosition(item, corpse.transform.position + new Vector3(0, 0.3f)));
}
}
Pool.FreeList(ref list);
delayedItems.Remove(steamID);
});
}
#endregion
#region Core
void DoSpawns(BasePlayer player)
{
var steamID = player.userID;
delayedItems[steamID] = Pool.GetList<Item>();
if (Random.Range(0.0f, 1.0f) <= settings.Meds.DropChance)
{
var meds = SpawnMeds();
if (meds != null)
{
delayedItems[steamID].Add(meds);
}
}
var definition = player.inventory?.containerBelt?.FindItemByUID(player.svActiveItemID)?.info;
if (definition == null)
{
return;
}
var itemWeapon = ItemManager.Create(definition, 1, settings.Guns.RandomSkin ? GetRandomSkin(definition) : 0uL);
if (itemWeapon == null)
{
return;
}
var condition = Random.Range(settings.Guns.Condition.Min, settings.Guns.Condition.Max);
itemWeapon.conditionNormalized = condition / 100;
var heldEnt = itemWeapon.GetHeldEntity();
if (heldEnt is BaseProjectile && Random.Range(0.0f, 1.0f) <= settings.Ammo.DropChance)
{
var ammo = SpawnAmmo(heldEnt as BaseProjectile);
if (ammo != null)
{
delayedItems[steamID].Add(ammo);
}
}
if (Random.Range(0.0f, 1.0f) <= settings.Guns.DropChance)
{
SetAttachments(itemWeapon);
if (settings.GunIntoCorpse || !player.eyes || !heldEnt)
{
delayedItems[steamID].Add(itemWeapon);
}
else
{
ApplyVelocity(DropNearPosition(itemWeapon, player.eyes.position));
}
}
}
Item SpawnMeds()
{
int amount = Random.Range((int)settings.Meds.Amount.Min, (int)settings.Meds.Amount.Max);
return amount < 1 ? null : ItemManager.CreateByName("syringe.medical", amount);
}
Item SpawnAmmo(BaseProjectile weapon)
{
if (!weapon.primaryMagazine.ammoType)
{
return null;
}
int amount = Random.Range((int)settings.Ammo.Amount.Min, (int)settings.Ammo.Amount.Max);
return amount < 1 ? null : ItemManager.Create(weapon.primaryMagazine.ammoType, amount);
}
Item SetAttachments(Item item)
{
if (settings.Guns.Attachments.Length < 1 || item.contents == null)
{
return item;
}
var attachmentCount = Random.Range(settings.Guns.AttachmentCount.Min, settings.Guns.AttachmentCount.Max);
for (int i = 0; i < attachmentCount; i++)
{
var attachment = settings.Guns.Attachments.GetRandom();
var attachmentItem = ItemManager.CreateByPartialName(attachment);
if (attachmentItem == null || !item.contents.CanTake(attachmentItem))
{
continue;
}
attachmentItem.MoveToContainer(item.contents);
}
return item;
}
BaseEntity DropNearPosition(Item item, Vector3 pos) => item.CreateWorldObject(pos);
BaseEntity ApplyVelocity(BaseEntity entity)
{
entity.SetVelocity(new Vector3(Random.Range(-4f, 4f), Random.Range(-0.3f, 2f), Random.Range(-4f, 4f)));
entity.SetAngularVelocity(
new Vector3(Random.Range(-10f, 10f),
Random.Range(-10f, 10f),
Random.Range(-10f, 10f))
);
entity.SendNetworkUpdateImmediate();
return entity;
}
ulong GetRandomSkin(ItemDefinition idef)
{
if (!idef)
return 0;
List<int> skins = Pool.GetList<int>();
if (idef.skins != null && idef.skins.Length > 0)
{
skins.AddRange(from skin in idef.skins select skin.id);
}
if (idef.skins2 != null && idef.skins2.Length > 0)
{
skins.AddRange(from skin in idef.skins2 where skin != null select (int)skin.WorkshopId);
}
var randomSkin = skins.GetRandom();
Pool.FreeList(ref skins);
return randomSkin == 0 ? 0 : ItemDefinition.FindSkin(idef.itemid, randomSkin);
}
#endregion
#region Configuration
protected override void LoadDefaultConfig()
{
settings = Settings.Default;
SaveConfig();
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
settings = Config.ReadObject<Settings>();
if (settings == null)
{
throw new Exception();
}
}
catch
{
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(settings);
class Settings
{
public GunSettings Guns { get; set; }
public OtherSettings Ammo { get; set; }
public OtherSettings Meds { get; set; }
[JsonProperty("Put weapon into corpse")]
public bool GunIntoCorpse { get; set; }
[JsonProperty("Remove default loot from corpse")]
public bool RemoveDefault { get; set; }
[JsonProperty("Drop spawned items near corpse (otherwise just delete them)")]
public bool DropNearFull { get; set; }
public static Settings Default => new Settings
{
Guns = new GunSettings
{
DropChance = 1.0f,
AttachmentCount = new RangeSettings
{
Min = 0,
Max = 2
},
Condition = new RangeSettings
{
Min = 5,
Max = 95
},
RandomSkin = true,
Attachments = new[]
{
"weapon.mod.8x.scope",
"weapon.mod.flashlight",
"weapon.mod.holosight",
"weapon.mod.lasersight",
"weapon.mod.muzzleboost",
"weapon.mod.muzzlebrake",
"weapon.mod.silencer",
"weapon.mod.simplesight",
"weapon.mod.small.scope"
}
},
Ammo = new OtherSettings
{
DropChance = 0.8f,
Amount = new RangeSettings
{
Min = 10,
Max = 55
}
},
Meds = new OtherSettings
{
DropChance = 0.4f,
Amount = new RangeSettings
{
Min = 1,
Max = 3
}
},
DropNearFull = true,
GunIntoCorpse = false,
RemoveDefault = false
};
public class GunSettings : DropChanceSettings
{
[JsonProperty("Gun condition")]
public RangeSettings Condition { get; set; }
[JsonProperty("Attachment count")]
public RangeSettings AttachmentCount { get; set; }
[JsonProperty("Attachment list")]
public string[] Attachments { get; set; }
[JsonProperty("Assign random skin")]
public bool RandomSkin { get; set; }
}
public class DropChanceSettings
{
[JsonProperty("Drop chance")]
public float DropChance { get; set; }
}
public class OtherSettings : DropChanceSettings
{
[JsonProperty("Amount to drop")]
public RangeSettings Amount { get; set; }
}
public class RangeSettings
{
public uint Min { get; set; }
public uint Max { get; set; }
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment