C#:
using Facepunch;
using Facepunch.Math;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Loot Defender", "Author Egor Blagov, Maintainer nivex", "2.2.4")]
[Description("Defends loot from other players who dealt less damage than you.")]
internal class LootDefender : RustPlugin {
[PluginReference]
Plugin PersonalHeli, Friends, Clans, RustRewards, HeliSignals, BradleyDrops, HelpfulSupply, ShoppyStock, XLevels, XPerience, SkillTree;
private static LootDefender Instance;
private static StringBuilder sb;
private const ulong supplyDropSkinID = 234501;
private const string bypassLootPerm = "lootdefender.bypass.loot";
private const string bypassDamagePerm = "lootdefender.bypass.damage";
private const string bypassLockoutsPerm = "lootdefender.bypass.lockouts";
private Dictionary<ulong, List<DamageKey>> _apcAttackers = new();
private Dictionary<ulong, List<DamageKey>> _heliAttackers = new();
private Dictionary<ulong, ulong> _locked {
get;
set;
}
= new();
private List<ulong> _personal {
get;
set;
}
= new();
private List<ulong> _boss {
get;
set;
}
= new();
private List<string> _sent {
get;
set;
}
= new();
private static StoredData data {
get;
set;
}
= new();
private MonumentInfo launchSite {
get;
set;
}
private List<MonumentInfo> harbors {
get;
set;
}
= new();
private List<ulong> ownerids = new() {
0, 1337422, 3566257, 123425345634634
}
;
public enum DamageEntryType {
Bradley, Corpse, Heli, NPC, None
}
public class Lockout {
public double Bradley {
get;
set;
}
public double Heli {
get;
set;
}
public bool Any() => Bradley > 0 || Heli > 0;
}
private class StoredData {
public Dictionary<string, Lockout> Lockouts {
get;
}
= new();
public Dictionary<string, UI.Info> UI {
get;
set;
}
= new();
[JsonProperty(PropertyName = "DamageInfo")]
public Dictionary<ulong, DamageInfo> Damage {
get;
set;
}
= new();
[JsonProperty(PropertyName = "LockInfo")]
public Dictionary<ulong, LockInfo> Lock {
get;
set;
}
= new();
public void Sanitize() {
foreach (var (uid, damageInfo) in Damage.ToList()) {
damageInfo._entity = BaseNetworkable.serverEntities.Find(new(uid)) as BaseEntity;
if (damageInfo.damageEntryType == DamageEntryType.NPC && !config.Npc.Enabled) {
if (damageInfo._entity.IsValid()) {
damageInfo._entity.OwnerID = 0uL;
}
Damage.Remove(uid);
}
else if (damageInfo._entity == null) {
Damage.Remove(uid);
}
else {
foreach (var x in damageInfo.damageKeys) {
x.attacker = BasePlayer.FindByID(x.userid);
}
damageInfo.Start();
}
}
foreach (var (uid, lockInfo) in Lock.ToList()) {
var entity = BaseNetworkable.serverEntities.Find(new(uid)) as BaseEntity;
if (lockInfo.damageInfo.damageEntryType == DamageEntryType.NPC && !config.Npc.Enabled) {
if (entity.IsValid()) {
entity.OwnerID = 0uL;
}
Lock.Remove(uid);
}
else if (entity == null) {
Lock.Remove(uid);
}
}
}
}
private class DamageEntry {
public float DamageDealt {
get;
set;
}
public DateTime Timestamp {
get;
set;
}
public string TeamID {
get;
set;
}
public DamageEntry() {
}
public DamageEntry(ulong teamID) {
Timestamp = DateTime.Now;
TeamID = teamID.ToString();
}
public bool IsOutdated(int timeout) => timeout > 0 && DateTime.Now.Subtract(Timestamp).TotalSeconds >= timeout;
}
private class DamageKey {
public ulong userid {
get;
set;
}
public string name {
get;
set;
}
public DamageEntry damageEntry {
get;
set;
}
internal BasePlayer attacker {
get;
set;
}
public DamageKey() {
}
public DamageKey(BasePlayer attacker) {
this.attacker = attacker;
userid = attacker.userID;
name = attacker.displayName;
}
}
private class DamageInfo {
public List<DamageKey> damageKeys {
get;
set;
}
= new();
[JsonIgnore]
public Dictionary<ulong, BasePlayer> interact {
get;
set;
}
= new();
private List<ulong> participants {
get;
set;
}
= new();
public DamageEntryType damageEntryType {
get;
set;
}
= DamageEntryType.None;
public string NPCName {
get;
set;
}
public ulong OwnerID {
get;
set;
}
public ulong SkinID {
get;
set;
}
public DateTime start {
get;
set;
}
internal int _lockTime {
get;
set;
}
[JsonIgnore]
internal BaseEntity _entity {
get;
set;
}
internal Vector3 _position {
get;
set;
}
internal Vector3 lastAttackedPosition {
get;
set;
}
internal ulong _uid {
get;
set;
}
[JsonIgnore]
internal Timer _timer {
get;
set;
}
internal List<DamageKey> keys {
get;
set;
}
= new();
List<DamageGroup> damageGroups;
internal float FullDamage {
get {
return damageKeys.Sum(x => x.damageEntry.DamageDealt);
}
}
public DamageInfo() {
}
public DamageInfo(DamageEntryType damageEntryType, string NPCName, BaseEntity entity, DateTime start) {
SkinID = entity.skinID;
_entity = entity;
_uid = entity.net.ID.Value;
this.damageEntryType = damageEntryType;
this.NPCName = NPCName;
this.start = start;
Start();
}
public void Start() {
_lockTime = GetLockTime(damageEntryType);
_timer = Instance.timer.Every(1f, CheckExpiration);
}
public void DestroyTimer() {
_timer?.Destroy();
}
private void CheckExpiration() {
damageKeys.ForEach(x => {
if (x.damageEntry.IsOutdated(_lockTime)) {
if (x.userid == OwnerID) {
Unlock();
}
keys.Add(x);
}
}
);
keys.ForEach(x => damageKeys.Remove(x));
keys.Clear();
}
public void Unlock() {
OwnerID = 0;
if (_entity != null) {
_entity.OwnerID = 0;
}
if (!Instance._locked.Remove(_uid)) {
return;
}
if (damageEntryType == DamageEntryType.Bradley && config.Bradley.Messages.NotifyChat) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "BradleyUnlocked", PositionToGrid(_position));
}
}
if (damageEntryType == DamageEntryType.Heli && config.Helicopter.Messages.NotifyChat) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "HeliUnlocked", PositionToGrid(_position));
}
}
if (damageEntryType == DamageEntryType.NPC && config.Npc.Messages.NotifyChat) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "NpcUnlocked", NPCName, PositionToGrid(_position));
}
}
}
private void Lock(BaseEntity entity, ulong id) {
Instance._locked[_uid] = entity.OwnerID = OwnerID = id;
_position = entity.transform.position;
}
public void AddDamage(BaseCombatEntity entity, BasePlayer attacker, DamageEntry entry, float amount) {
entry.DamageDealt += amount;
entry.Timestamp = DateTime.Now;
_position = entity.transform.position;
lastAttackedPosition = attacker.transform.position;
if (SkinID == 0) {
SkinID = entity.skinID;
}
if (damageEntryType == DamageEntryType.NPC && !Instance.CanLockNpc(entity)) {
return;
}
if (damageEntryType == DamageEntryType.Bradley && !Instance.CanLockBradley(entity)) {
return;
}
if (entity.OwnerID.IsSteamId()) {
OwnerID = entity.OwnerID;
}
if (OwnerID != 0uL) {
return;
}
float damage = 0f;
var grid = PositionToGrid(entity.transform.position);
if (entry.TeamID != "0") {
foreach (var x in damageKeys) {
if (x.damageEntry.TeamID == entry.TeamID) {
damage += x.damageEntry.DamageDealt;
}
}
}
else damage = entry.DamageDealt;
if (config.Helicopter.Threshold > 0f && entity is PatrolHelicopter) {
if (damage >= entity.MaxHealth() * config.Helicopter.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypasshelilock")) {
if (config.Helicopter.Messages.NotifyLocked == true) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "Locked Heli", grid, attacker.displayName);
}
}
Lock(entity, attacker.userID);
}
}
else if (config.Bradley.Threshold > 0f && entity is BradleyAPC && Instance.CanLockBradley(entity)) {
if (damage >= entity.MaxHealth() * config.Bradley.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypassbradleylock")) {
if (config.Bradley.Messages.NotifyLocked == true) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "Locked Bradley", grid, attacker.displayName);
}
}
Lock(entity, attacker.userID);
}
}
else if (config.Npc.Threshold > 0f && entity is BasePlayer npc && Instance.CanLockNpc(npc)) {
if (!npc.userID.IsSteamId() && damage >= entity.MaxHealth() * config.Npc.Threshold && !Instance.HasPermission(attacker, "lootdefender.bypassnpclock")) {
if (config.Npc.Messages.NotifyLocked == true) {
foreach (var target in BasePlayer.activePlayerList) {
CreateMessage(target, "Locked Npc", grid, npc.displayName, attacker.displayName);
}
}
Lock(entity, attacker.userID);
}
}
}
public DamageEntry TryGet(ulong id) {
foreach (var x in damageKeys) {
if (x.userid == id) {
return x.damageEntry;
}
}
return null;
}
public DamageEntry Get(BasePlayer attacker) {
DamageEntry entry = TryGet(attacker.userID);
if (entry == null) {
damageKeys.Add(new(attacker) {
damageEntry = entry = new(attacker.currentTeam),
}
);
}
return entry;
}
public bool isKilled;
public void OnKilled(Vector3 position, HitInfo hitInfo, float distance) {
if (isKilled) return;
isKilled = true;
SetCanInteract();
DisplayDamageReport();
FindLooters(position, hitInfo, distance);
}
private void FindLooters(Vector3 position, HitInfo hitInfo, float distance) {
var weapon = hitInfo?.Weapon?.GetItem()?.info?.shortname ?? hitInfo?.WeaponPrefab?.ShortPrefabName ?? "";
HashSet<ulong> looters = new();
HashSet<ulong> users = new();
foreach (var x in damageKeys) {
if (CanInteract(x.userid, x.attacker)) {
if (TryGet(x.userid)?.DamageDealt > 0) {
users.Add(x.userid);
}
looters.Add(x.userid);
}
}
foreach (var userid in users) {
Instance.GiveXpReward(_entity, this, userid, weapon, distance, users.Count);
Instance.GiveRustReward(_entity, this, userid, weapon, users.Count);
Instance.GiveShopReward(_entity, this, userid, weapon, distance, users.Count);
}
if (damageEntryType == DamageEntryType.Bradley || damageEntryType == DamageEntryType.Heli) {
Instance.LockoutLooters(looters, position, damageEntryType, SkinID);
}
}
public void DisplayDamageReport() {
if (damageEntryType == DamageEntryType.Bradley || damageEntryType == DamageEntryType.Heli) {
foreach (var target in BasePlayer.activePlayerList) {
if (CanDisplayReport(target)) {
Message(target, GetDamageReport(target.userID));
}
}
}
else if (damageEntryType == DamageEntryType.NPC) {
foreach (var x in damageKeys) {
if (CanDisplayReport(x.attacker)) {
Message(x.attacker, GetDamageReport(x.userid));
}
}
}
}
private bool CanDisplayReport(BasePlayer target) {
if (target == null || !target.IsConnected || damageEntryType == DamageEntryType.None) {
return false;
}
if (damageEntryType == DamageEntryType.Bradley) {
if (config.Bradley.Messages.NotifyKiller && IsParticipant(target.userID, target)) {
return true;
}
return config.Bradley.Messages.NotifyChat;
}
if (damageEntryType == DamageEntryType.Heli) {
if (config.Helicopter.Messages.NotifyKiller && IsParticipant(target.userID, target)) {
return true;
}
return config.Helicopter.Messages.NotifyChat;
}
if (damageEntryType == DamageEntryType.NPC) {
if (config.Npc.Messages.NotifyKiller && IsParticipant(target.userID, target)) {
return true;
}
return config.Npc.Messages.NotifyChat;
}
return false;
}
public void SetCanInteract() {
var damageGroups = GetDamageGroups();
var topDamageGroups = GetTopDamageGroups(damageGroups, damageEntryType);
if (damageGroups.Count > 0) {
foreach (var damageGroup in damageGroups) {
if (topDamageGroups.Contains(damageGroup) || Instance.IsAlly(OwnerID, damageGroup.FirstDamagerDealer.userid)) {
interact[damageGroup.FirstDamagerDealer.userid] = damageGroup.FirstDamagerDealer.attacker;
}
else {
var damage = TryGet(damageGroup.FirstDamagerDealer.userid)?.DamageDealt ?? 0f;
var dmgRatio = damage > 0 && FullDamage > 0 ? damage / FullDamage : 0;
float threshold = config.Npc.Threshold;
if (damageEntryType == DamageEntryType.Bradley) {
threshold = config.Bradley.Threshold;
}
else if (damageEntryType == DamageEntryType.Heli) {
threshold = config.Helicopter.Threshold;
}
if (OwnerID == 0 && dmgRatio >= threshold) {
OwnerID = damageGroup.FirstDamagerDealer.userid;
interact[damageGroup.FirstDamagerDealer.userid] = damageGroup.FirstDamagerDealer.attacker;
}
else {
participants.Add(damageGroup.FirstDamagerDealer.userid);
}
}
}
}
this.damageGroups = damageGroups;
}
public string GetDamageReport(ulong targetId) {
var userid = targetId.ToString();
var nameKey = damageEntryType == DamageEntryType.Bradley ? _("BradleyAPC", userid) : damageEntryType == DamageEntryType.Heli ? _("Helicopter", userid) : NPCName;
var firstDamageDealer = string.Empty;
sb.Length = 0;
sb.AppendLine($"{_("DamageReport", userid, $"<color= {
config.Report.Ok
}
> {
nameKey
}
</color>")}:");
if (damageEntryType == DamageEntryType.Bradley || damageEntryType == DamageEntryType.Heli) {
var seconds = Math.Ceiling((DateTime.Now - start).TotalSeconds);
sb.AppendLine($"{_("DamageTime", userid, nameKey, seconds)}");
}
if (damageGroups.Count > 0) {
foreach (var damageGroup in damageGroups) {
if (interact.ContainsKey(damageGroup.FirstDamagerDealer.userid)) {
sb.Append($"<color={config.Report.Ok}>√</color> ");
firstDamageDealer = damageGroup.FirstDamagerDealer.name;
}
else {
sb.Append($"<color={config.Report.NotOk}>X</color> ");
}
sb.Append($"{damageGroup.ToReport(damageGroup.FirstDamagerDealer, this)}\n");
}
if (damageEntryType == DamageEntryType.NPC && !string.IsNullOrEmpty(firstDamageDealer) && damageGroups.Count > 1) {
sb.Append($" {_("FirstLock", userid, firstDamageDealer, config.Npc.Threshold * 100f)}");
}
}
return sb.ToString();
}
public bool IsParticipant(ulong userid, BasePlayer player) {
return participants.Contains(userid) || CanInteract(userid, player);
}
public bool CanInteract(ulong userid, BasePlayer player) {
if (damageEntryType == DamageEntryType.NPC && !config.Npc.Enabled) {
return true;
}
if (damageGroups == null) {
interact.Clear();
participants.Clear();
SetCanInteract();
}
if (interact.Count == 0 || interact.ContainsKey(userid)) {
return true;
}
if (Instance.IsAlly(userid, OwnerID)) {
interact.Add(userid, player);
return true;
}
return false;
}
private List<DamageGroup> GetTopDamageGroups(List<DamageGroup> damageGroups, DamageEntryType damageEntryType) {
List<DamageGroup> topDamageGroups = new();
if (damageGroups.Count == 0) {
return topDamageGroups;
}
var topDamageGroup = damageGroups.OrderByDescending(x => x.TotalDamage).First();
foreach (var damageGroup in damageGroups) {
foreach (var playerId in damageGroup.Players) {
if (Instance.IsAlly(playerId, OwnerID)) {
topDamageGroups.Add(damageGroup);
break;
}
}
}
return topDamageGroups;
}
private List<DamageGroup> GetDamageGroups() {
List<DamageGroup> damageGroups = new();
foreach (var x in damageKeys) {
damageGroups.Add(new(x));
}
damageGroups.Sort((x, y) => y.TotalDamage.CompareTo(x.TotalDamage));
return damageGroups;
}
}
private class LockInfo {
public DamageInfo damageInfo {
get;
set;
}
private DateTime LockTimestamp {
get;
set;
}
private int LockTimeout {
get;
set;
}
internal bool IsLockOutdated => LockTimeout > 0 && DateTime.Now.Subtract(LockTimestamp).TotalSeconds >= LockTimeout;
public LockInfo() {
}
public LockInfo(DamageInfo damageInfo, int lockTimeout) {
LockTimestamp = DateTime.Now;
LockTimeout = lockTimeout;
this.damageInfo = damageInfo;
}
public bool CanInteract(ulong userId, BasePlayer target) => damageInfo.CanInteract(userId, target);
public string GetDamageReport(ulong userId) => damageInfo.GetDamageReport(userId);
}
private class DamageGroup {
public float TotalDamage {
get;
set;
}
public DamageKey FirstDamagerDealer {
get;
set;
}
private List<ulong> additionalPlayers {
get;
set;
}
= new();
[JsonIgnore]
public List<ulong> Players {
get {
List<ulong> players = new() {
FirstDamagerDealer.userid
}
;
foreach (var targetId in additionalPlayers) {
if (!players.Contains(targetId)) {
players.Add(targetId);
}
}
return players;
}
}
public DamageGroup() {
}
public DamageGroup(DamageKey x) {
TotalDamage = x.damageEntry.DamageDealt;
FirstDamagerDealer = x;
if (RelationshipManager.ServerInstance.playerToTeam.TryGetValue(x.userid, out var team)) {
for (int i = 0; i < team.members.Count; i++) {
ulong member = team.members[i];
if (member == x.userid || additionalPlayers.Contains(member)) {
continue;
}
additionalPlayers.Add(member);
}
}
// add clan
}
public string ToReport(DamageKey damageKey, DamageInfo damageInfo) {
var damage = damageInfo.TryGet(damageKey.userid)?.DamageDealt ?? 0f;
var percent = damage > 0 && damageInfo.FullDamage > 0 ? damage / damageInfo.FullDamage * 100 : 0;
var color = additionalPlayers.Count == 0 ? config.Report.SinglePlayer : config.Report.Team;
var damageLine = _("Format", damageKey.userid.ToString(), damage, percent);
return $"<color={color}>{damageKey.name}</color> {damageLine}";
}
}
#region Hooks
private void OnServerSave() {
timer.Once(15f, SaveData);
}
private void Init() {
Unsubscribe(nameof(OnEventTrigger));
Unsubscribe();
Instance = this;
sb = new();
if (!string.IsNullOrEmpty(config.Lockout.Command)) AddCovalenceCommand(config.Lockout.Command, nameof(CommandLockouts));
if (!string.IsNullOrEmpty(config.UI.Command)) AddCovalenceCommand(config.UI.Command, nameof(CommandUI));
AddCovalenceCommand("lo", nameof(CommandLootDefender));
RegisterPermissions();
LoadData();
}
private void OnServerInitialized(bool serverinit) {
if (config.Hackable.Enabled) {
Subscribe(nameof(CanHackCrate));
Subscribe(nameof(OnGuardedCrateEventEnded));
}
if (config.SupplyDrop.Lock) {
if (config.SupplyDrop.LockTime > 0) {
if (config.SupplyDrop.NpcRandomRaids) {
Subscribe(nameof(OnRandomRaidWin));
}
Subscribe(nameof(OnSupplyDropLanded));
}
if (config.SupplyDrop.Excavator) {
Subscribe(nameof(OnExcavatorSuppliesRequested));
}
if (config.SupplyDrop.HelpfulSupply && HelpfulSupply != null) {
Subscribe(nameof(OnEntitySpawned));
}
Subscribe(nameof(OnExplosiveDropped));
Subscribe(nameof(OnExplosiveThrown));
Subscribe(nameof(OnSupplyDropDropped));
Subscribe(nameof(OnCargoPlaneSignaled));
}
if (config.SupplyDrop.DestroyTime > 0f || config.CH47Gibs) {
Subscribe(nameof(OnEntitySpawned));
}
if (config.Npc.BossMonster) {
Unsubscribe(nameof(OnBossSpawn));
Unsubscribe(nameof(OnBossKilled));
}
if (!config.Bradley.LockPersonal) {
Subscribe(nameof(OnPersonalApcSpawned));
}
if (!config.Helicopter.LockPersonal) {
Subscribe(nameof(OnPersonalHeliSpawned));
}
if (config.UI.Bradley.Enabled || config.UI.Heli.Enabled) {
foreach (var player in BasePlayer.activePlayerList) {
UI.ShowLockouts(player);
}
Subscribe(nameof(OnPlayerSleepEnded));
}
if (config.Bradley.Threshold != 0f || config.Helicopter.Threshold != 0f || config.Hackable.Laptop) {
Subscribe(nameof(OnPlayerAttack));
}
if (!config.SupplyDrop.Skins.Contains(0uL)) {
config.SupplyDrop.Skins.Add(0uL);
}
if (config.Lockout.F15) {
Subscribe(nameof(OnEventTrigger));
}
Subscribe(nameof(OnEntityTakeDamage));
Subscribe(nameof(OnEntityDeath));
Subscribe(nameof(OnEntityKill));
Subscribe(nameof(CanLootEntity));
Subscribe(nameof(CanBradleyTakeDamage));
SetupLaunchSite();
}
private bool IsF15EventActive;
private void OnEventTrigger(TriggeredEventPrefab prefab) {
if (config.Lockout.F15 && !IsF15EventActive && prefab.name == "assets/bundled/prefabs/world/event_f15e.prefab") {
Puts("F15 event has started; bypassing player lockouts!");
IsF15EventActive = true;
}
}
private void Unload() {
UI.DestroyAllLockoutUI();
SaveData();
Instance = null;
data = null;
sb = null;
}
private void OnPlayerSleepEnded(BasePlayer player) {
UI.DestroyLockoutUI(player);
UI.ShowLockouts(player);
}
private object OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) => HandleTeam(team, player.userID);
private object OnTeamKick(RelationshipManager.PlayerTeam team, BasePlayer player, ulong targetId) => HandleTeam(team, targetId);
private object OnPlayerAttack(BasePlayer attacker, HitInfo hitInfo) {
if (attacker == null || HasPermission(attacker, bypassDamagePerm) || hitInfo == null) {
return null;
}
if (config.Hackable.Laptop && hitInfo.HitBone == 242862488 && hitInfo.HitEntity is HackableLockedCrate crate && IsDefended(crate)) // laptopcollision
{
hitInfo.HitBone = 0;
return null;
}
if (config.Bradley.Threshold != 0f || config.Helicopter.Threshold != 0f) {
if (hitInfo.HitEntity is ServerGib gibs && gibs.IsValid() && data.Lock.TryGetValue(gibs.net.ID.Value, out var lockInfo)) {
if (gibs.OwnerID != 0 && !lockInfo.IsLockOutdated) {
if (!lockInfo.CanInteract(attacker.userID, attacker)) {
if (CanMessage(attacker)) {
CreateMessage(attacker, "CannotMine");
Message(attacker, lockInfo.GetDamageReport(attacker.userID));
}
CancelDamage(hitInfo);
return false;
}
}
else {
data.Lock.Remove(gibs.net.ID.Value);
gibs.OwnerID = 0;
}
}
}
return null;
}
private object OnEntityTakeDamage(PatrolHelicopter heli, HitInfo hitInfo) {
if (config.Helicopter.Threshold == 0f || !heli.IsValid() || _personal.Contains(heli.net.ID.Value) || hitInfo == null || heli.myAI == null || heli.myAI.isDead || CanHeliTakeDamage(heli, hitInfo) != null) {
return null;
}
return OnEntityTakeDamageHandler(heli, hitInfo, DamageEntryType.Heli, string.Empty);
}
private object OnEntityTakeDamage(BradleyAPC apc, HitInfo hitInfo) {
if (config.Bradley.Threshold == 0f || !apc.IsValid() || hitInfo == null || CanBradleyTakeDamage(apc, hitInfo) != null) {
return null;
}
return OnEntityTakeDamageHandler(apc, hitInfo, DamageEntryType.Bradley, string.Empty);
}
private object OnEntityTakeDamage(BasePlayer player, HitInfo hitInfo) {
if (!config.Npc.Enabled || !player.IsValid() || player.userID.IsSteamId() || hitInfo == null) {
return null;
}
if (config.Npc.Min > 0 && player.startHealth < config.Npc.Min) {
return null;
}
return OnEntityTakeDamageHandler(player, hitInfo, DamageEntryType.NPC, player.displayName);
}
private object OnEntityTakeDamageHandler(BaseCombatEntity entity, HitInfo hitInfo, DamageEntryType damageEntryType, string npcName) {
if (!config.Bradley.LockConvoy && entity.skinID == 755446 && entity is BradleyAPC) {
return null;
}
if (!config.Helicopter.LockConvoy && entity.skinID == 755446 && entity is PatrolHelicopter) {
return null;
}
if (!(hitInfo.Initiator is BasePlayer attacker) || !attacker.userID.IsSteamId()) {
return null;
}
if (_locked.TryGetValue(entity.net.ID.Value, out var ownerId) && !HasPermission(attacker, bypassDamagePerm) && !IsAlly(attacker.userID, ownerId)) {
if (!BlockDamage(damageEntryType)) {
return null;
}
if (CanMessage(attacker)) {
CreateMessage(attacker, "CannotDamageThis");
}
CancelDamage(hitInfo);
return true;
}
if (!data.Damage.TryGetValue(entity.net.ID.Value, out var damageInfo)) {
data.Damage[entity.net.ID.Value] = damageInfo = new(damageEntryType, npcName, entity, DateTime.Now);
}
DamageEntry entry = damageInfo.Get(attacker);
float total = hitInfo.damageTypes.Total();
if (hitInfo.isHeadshot) total *= 2f;
damageInfo.AddDamage(entity, attacker, entry, total);
if (damageEntryType == DamageEntryType.Heli) {
float prevHealth = entity.health;
NextTick(() => {
if (entity == null) {
return;
}
damageInfo.AddDamage(entity, attacker, entry, Mathf.Abs(prevHealth - entity.health));
}
);
}
return null;
}
public static bool IsKilled(BaseNetworkable a) => a == null || a.IsDestroyed || !a.isSpawned;
private bool BlockDamage(DamageEntryType damageEntryType) {
if (damageEntryType == DamageEntryType.NPC && config.Npc.LootingOnly) {
return false;
}
else if (damageEntryType == DamageEntryType.Heli && config.Helicopter.LootingOnly) {
return false;
}
else if (damageEntryType == DamageEntryType.Bradley && config.Bradley.LootingOnly) {
return false;
}
return true;
}
private object CanBradleyTakeDamage(BradleyAPC apc, HitInfo hitInfo) {
if (config.Lockout.Bradley <= 0 || !apc.IsValid() || !(hitInfo.Initiator is BasePlayer attacker)) {
return null;
}
if (HasLockout(attacker, DamageEntryType.Bradley, apc.skinID)) {
CancelDamage(hitInfo);
return false;
}
if (!data.Lockouts.ContainsKey(attacker.UserIDString)) {
if (!_apcAttackers.TryGetValue(apc.net.ID.Value, out var attackers)) {
_apcAttackers[apc.net.ID.Value] = attackers = new();
}
if (!attackers.Exists(x => x.userid == attacker.userID)) {
attackers.Add(new(attacker));
}
}
return null;
}
private object CanHeliTakeDamage(PatrolHelicopter heli, HitInfo hitInfo) {
if (config.Lockout.Heli <= 0 || !heli.IsValid() || !(hitInfo.Initiator is BasePlayer attacker)) {
return null;
}
if (HasLockout(attacker, DamageEntryType.Heli, heli.skinID)) {
CancelDamage(hitInfo);
return false;
}
if (!data.Lockouts.ContainsKey(attacker.UserIDString)) {
if (!_heliAttackers.TryGetValue(heli.net.ID.Value, out var attackers)) {
_heliAttackers[heli.net.ID.Value] = attackers = new();
}
if (!attackers.Exists(x => x.userid == attacker.userID)) {
attackers.Add(new(attacker));
}
}
return null;
}
private void OnEntityDeath(PatrolHelicopter heli, HitInfo hitInfo) {
if (!heli.IsValid()) {
return;
}
_personal.Remove(heli.net.ID.Value);
_heliAttackers.Remove(heli.net.ID.Value);
OnEntityDeathHandler(heli, DamageEntryType.Heli, hitInfo);
}
private void OnEntityKill(PatrolHelicopter heli) => OnEntityDeath(heli, null);
private void OnEntityDeath(BradleyAPC apc, HitInfo hitInfo) {
if (!apc.IsValid()) {
return;
}
_apcAttackers.Remove(apc.net.ID.Value);
OnEntityDeathHandler(apc, DamageEntryType.Bradley, hitInfo);
}
private void OnEntityDeath(BasePlayer player, HitInfo hitInfo) {
if (!config.Npc.Enabled || !player.IsValid() || player.userID.IsSteamId()) {
return;
}
OnEntityDeathHandler(player, DamageEntryType.NPC, hitInfo);
}
private void OnEntityDeath(NPCPlayerCorpse corpse, HitInfo hitInfo) {
if (!config.Npc.Enabled || !corpse.IsValid()) {
return;
}
OnEntityDeathHandler(corpse, DamageEntryType.Corpse, hitInfo);
}
private void OnEntityKill(NPCPlayerCorpse corpse) => OnEntityDeath(corpse, null);
private bool IsInBounds(MonumentInfo monument, Vector3 target) {
return monument.IsInBounds(target) || new OBB(monument.transform.position, monument.transform.rotation, new Bounds(monument.Bounds.center, new Vector3(300f, 300f, 300f))).Contains(target);
}
private bool CanLockBradley(BaseEntity entity) {
if (config.Bradley.Threshold <= 0f || _personal.Contains(entity.net.ID.Value)) {
return false;
}
if (BradleyDrops && BradleyDrops.CallHook("IsBradleyDrop", entity.skinID) != null) {
return false;
}
if (entity.name.Contains($"BradleyApc[{entity.net.ID}]")) {
return config.Bradley.LockBradleyTiers;
}
if (entity.skinID != 0) {
if (entity.skinID == 755446) {
return config.Bradley.LockConvoy;
}
if (entity.skinID == 81182151852251420) {
return config.Bradley.LockHarbor;
}
if (entity.skinID == 8675309) {
return config.Bradley.LockMonument;
}
return false;
}
if (launchSite != null && IsInBounds(launchSite, entity.ServerPosition)) {
return config.Bradley.LockLaunchSite;
}
if (harbors.Exists(mi => IsInBounds(mi, entity.ServerPosition))) {
return config.Bradley.LockHarbor;
}
return config.Bradley.LockWorldly;
}
private bool CanLockHeli(BaseCombatEntity entity) {
if (HeliSignals && HeliSignals.CallHook("IsHeliSignalObject", entity.skinID) != null) {
return false;
}
if (entity.skinID != 0) {
if (entity.skinID == 755446) {
return config.Helicopter.LockConvoy;
}
if (entity.skinID == 81182151852251420) {
return config.Helicopter.LockHarbor == true;
}
return false;
}
return config.Helicopter.Threshold > 0f;
}
private bool CanLockNpc(BaseEntity entity) {
if (entity.OwnerID.ToString().Length == 5) {
return false;
}
return config.Npc.Threshold > 0f && !_boss.Contains(entity.net.ID.Value);
}
private void OnEntityDeathHandler(BaseCombatEntity entity, DamageEntryType damageEntryType, HitInfo hitInfo) {
if (data.Damage.TryGetValue(entity.net.ID.Value, out var damageInfo) && !damageInfo.isKilled) {
if (damageEntryType == DamageEntryType.Bradley && !CanLockBradley(entity)) return;
if (damageEntryType == DamageEntryType.Heli && !CanLockHeli(entity)) return;
if (damageEntryType == DamageEntryType.NPC && !CanLockNpc(entity)) return;
if (damageEntryType == DamageEntryType.Bradley || damageEntryType == DamageEntryType.Heli) {
var lockInfo = new LockInfo(damageInfo, damageEntryType == DamageEntryType.Heli ? config.Helicopter.LockTime : config.Bradley.LockTime);
var position = entity.transform.position;
if (entity is PatrolHelicopter && (position - Vector3.zero).magnitude > World.Size / 1.25f) {
return;
}
damageInfo.OnKilled(position, hitInfo, hitInfo?.ProjectileDistance ?? Vector3.Distance(position, damageInfo.lastAttackedPosition));
timer.Once(0.1f, () => {
LockInRadius<LockedByEntCrate>(position, lockInfo, damageEntryType);
LockInRadius<HelicopterDebris>(position, lockInfo, damageEntryType);
RemoveFireFromCrates(position, damageEntryType);
}
);
damageInfo.DestroyTimer();
}
else if (damageEntryType == DamageEntryType.NPC && config.Npc.Enabled && entity is BasePlayer npc) {
var position = entity.transform.position;
var npcId = npc.userID;
var damageKey = npc.net.ID.Value;
damageInfo.OnKilled(position, hitInfo, hitInfo?.ProjectileDistance ?? Vector3.Distance(position, damageInfo.lastAttackedPosition));
damageInfo.DestroyTimer();
timer.Once(0.1f, () => LockInRadius(position, damageKey, damageInfo, npcId));
}
else if (damageEntryType == DamageEntryType.NPC && config.Npc.Enabled && entity is LootableCorpse corpse) {
var position = entity.transform.position;
var npcId = corpse.playerSteamID;
var damageKey = corpse.net.ID.Value;
damageInfo.OnKilled(position, hitInfo, hitInfo?.ProjectileDistance ?? Vector3.Distance(position, damageInfo.lastAttackedPosition));
damageInfo.DestroyTimer();
timer.Once(0.1f, () => LockInRadius(position, damageKey, damageInfo, npcId));
}
}
if (data.Lock.Remove(entity.net.ID.Value, out var lockInfo2) && damageEntryType == DamageEntryType.Corpse && config.Npc.Enabled && entity is LootableCorpse corpse2) {
var corpsePos = corpse2.transform.position;
var corpseId = corpse2.playerSteamID;
timer.Once(0.1f, () => LockInRadius(corpsePos, lockInfo2, corpseId));
}
}
//void OnEntitySpawned(DroppedItemContainer container)
//{
// if (!config.Npc.Enabled || !container.IsValid() || container.playerSteamID.IsSteamId() || data.Lock.ContainsKey(container.net.ID.Value))
// {
// return;
// }
// foreach (var damageInfo in data.Damage.Values)
// {
// if (damageInfo.damageEntryType == DamageEntryType.NPC && container.Distance(damageInfo._position) <= 3f)
// {
// if (config.Npc.LockTime > 0f)
// {
// var uid = container.net.ID.Value;
// timer.Once(config.Npc.LockTime, () => data.Lock.Remove(uid));
// container.Invoke(() => container.OwnerID = 0, config.Npc.LockTime);
// }
// ulong ownerid = damageInfo.OwnerID;
// container.OwnerID = ownerid;
// container.Invoke(() => container.OwnerID = ownerid, 1f);
// data.Lock[container.net.ID.Value] = new(damageInfo, config.Npc.LockTime);
// }
// }
//}
void GiveRustReward(BaseEntity entity, DamageInfo damageInfo, ulong userid, string weapon, int total) {
if (RustRewards == null) {
return;
}
BasePlayer attacker = BasePlayer.FindByID(userid);
if (attacker == null || !attacker.userID.IsSteamId()) {
return;
}
var amount = damageInfo.damageEntryType == DamageEntryType.Bradley ? config.Bradley.RRP : damageInfo.damageEntryType == DamageEntryType.Heli ? config.Helicopter.RRP : config.Npc.RRP;
if (amount <= 0.0) {
return;
}
var distance = Vector3.Distance(attacker.transform.position, entity.transform.position);
// ... [Code preview truncated due to length. Full source in attached file] ...